github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/kbfs_ops.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  	"sync"
    10  	"time"
    11  
    12  	"github.com/keybase/client/go/kbfs/data"
    13  	"github.com/keybase/client/go/kbfs/env"
    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/kbfsedits"
    18  	"github.com/keybase/client/go/kbfs/kbfsmd"
    19  	"github.com/keybase/client/go/kbfs/kbfssync"
    20  	"github.com/keybase/client/go/kbfs/tlf"
    21  	"github.com/keybase/client/go/kbfs/tlfhandle"
    22  	"github.com/keybase/client/go/logger"
    23  	"github.com/keybase/client/go/protocol/chat1"
    24  	"github.com/keybase/client/go/protocol/keybase1"
    25  	"github.com/pkg/errors"
    26  	"golang.org/x/net/context"
    27  )
    28  
    29  const (
    30  	quotaUsageStaleTolerance = 10 * time.Second
    31  	initEditBlockTimeout     = 500 * time.Millisecond
    32  )
    33  
    34  // KBFSOpsStandard implements the KBFSOps interface, and is go-routine
    35  // safe by forwarding requests to individual per-folder-branch
    36  // handlers that are go-routine-safe.
    37  type KBFSOpsStandard struct {
    38  	appStateUpdater env.AppStateUpdater
    39  	config          Config
    40  	log             logger.Logger
    41  	deferLog        logger.Logger
    42  	ops             map[data.FolderBranch]*folderBranchOps
    43  	opsByFav        map[favorites.Folder]*folderBranchOps
    44  	opsLock         sync.RWMutex
    45  	// reIdentifyControlChan controls reidentification.
    46  	// Sending a value to this channel forces all fbos
    47  	// to be marked for revalidation.
    48  	// Closing this channel will shutdown the reidentification
    49  	// watcher.
    50  	reIdentifyControlChan chan chan<- struct{}
    51  	initDoneCh            <-chan struct{}
    52  
    53  	favs *Favorites
    54  
    55  	syncedTlfObservers *syncedTlfObserverList
    56  
    57  	editActivity kbfssync.RepeatedWaitGroup
    58  	editLock     sync.Mutex
    59  	editShutdown bool
    60  
    61  	currentStatus            *kbfsCurrentStatus
    62  	longOperationDebugDumper *ImpatientDebugDumper
    63  
    64  	initLock       sync.Mutex
    65  	initEditCancel context.CancelFunc
    66  	initEditReq    chan struct{}
    67  	initEditDone   chan struct{}
    68  	initSyncCancel context.CancelFunc
    69  }
    70  
    71  var _ KBFSOps = (*KBFSOpsStandard)(nil)
    72  
    73  const longOperationDebugDumpDuration = time.Minute
    74  
    75  type ctxKBFSOpsSkipEditHistoryBlockType int
    76  
    77  // This context key indicates that we should not block on TLF edit
    78  // history initialization, as it would cause a deadlock.
    79  const ctxKBFSOpsSkipEditHistoryBlock ctxKBFSOpsSkipEditHistoryBlockType = 1
    80  
    81  // NewKBFSOpsStandard constructs a new KBFSOpsStandard object.
    82  // `initDone` should be closed when the rest of initialization (such
    83  // as journal initialization) has completed.
    84  func NewKBFSOpsStandard(
    85  	appStateUpdater env.AppStateUpdater, config Config,
    86  	initDoneCh <-chan struct{}) *KBFSOpsStandard {
    87  	log := config.MakeLogger("")
    88  	kops := &KBFSOpsStandard{
    89  		appStateUpdater:       appStateUpdater,
    90  		config:                config,
    91  		log:                   log,
    92  		deferLog:              log.CloneWithAddedDepth(1),
    93  		ops:                   make(map[data.FolderBranch]*folderBranchOps),
    94  		opsByFav:              make(map[favorites.Folder]*folderBranchOps),
    95  		reIdentifyControlChan: make(chan chan<- struct{}),
    96  		initDoneCh:            initDoneCh,
    97  		favs:                  NewFavorites(config),
    98  		syncedTlfObservers:    newSyncedTlfObserverList(),
    99  		longOperationDebugDumper: NewImpatientDebugDumper(
   100  			config, longOperationDebugDumpDuration),
   101  		currentStatus: &kbfsCurrentStatus{},
   102  	}
   103  	kops.currentStatus.Init()
   104  	go kops.markForReIdentifyIfNeededLoop()
   105  	return kops
   106  }
   107  
   108  func (fs *KBFSOpsStandard) markForReIdentifyIfNeededLoop() {
   109  	maxValid := fs.config.TLFValidDuration()
   110  	// Tests and some users fail to set this properly.
   111  	if maxValid <= 10*time.Second || maxValid > 24*365*time.Hour {
   112  		maxValid = tlfValidDurationDefault
   113  	}
   114  	// Tick ten times the rate of valid duration allowing only overflows of +-10%
   115  	ticker := time.NewTicker(maxValid / 10)
   116  	for {
   117  		var now time.Time
   118  		var returnCh chan<- struct{}
   119  		var ok bool
   120  		select {
   121  		// Normal case: feed the current time from config and mark fbos needing
   122  		// validation.
   123  		case <-ticker.C:
   124  			now = fs.config.Clock().Now()
   125  		// Mark everything for reidentification via now being the empty value or
   126  		// quit.
   127  		case returnCh, ok = <-fs.reIdentifyControlChan:
   128  			if !ok {
   129  				ticker.Stop()
   130  				return
   131  			}
   132  		}
   133  		fs.markForReIdentifyIfNeeded(now, maxValid)
   134  		if returnCh != nil {
   135  			returnCh <- struct{}{}
   136  		}
   137  	}
   138  }
   139  
   140  func (fs *KBFSOpsStandard) markForReIdentifyIfNeeded(
   141  	now time.Time, maxValid time.Duration) {
   142  	fs.opsLock.Lock()
   143  	defer fs.opsLock.Unlock()
   144  
   145  	for _, fbo := range fs.ops {
   146  		fbo.markForReIdentifyIfNeeded(now, maxValid)
   147  	}
   148  }
   149  
   150  func (fs *KBFSOpsStandard) shutdownEdits(ctx context.Context) error {
   151  	fs.editLock.Lock()
   152  	fs.editShutdown = true
   153  	fs.editLock.Unlock()
   154  
   155  	err := fs.editActivity.Wait(ctx)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	return nil
   160  }
   161  
   162  // Shutdown safely shuts down any background goroutines that may have
   163  // been launched by KBFSOpsStandard.
   164  func (fs *KBFSOpsStandard) Shutdown(ctx context.Context) error {
   165  	defer fs.longOperationDebugDumper.Shutdown() // shut it down last
   166  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   167  	defer timeTrackerDone()
   168  
   169  	err := fs.shutdownEdits(ctx)
   170  	if err != nil {
   171  		return err
   172  	}
   173  
   174  	close(fs.reIdentifyControlChan)
   175  	var errors []error
   176  	if err := fs.favs.Shutdown(); err != nil {
   177  		errors = append(errors, err)
   178  	}
   179  	for _, ops := range fs.ops {
   180  		if err := ops.Shutdown(ctx); err != nil {
   181  			errors = append(errors, err)
   182  			// Continue on and try to shut down the other FBOs.
   183  		}
   184  	}
   185  	if len(errors) == 1 {
   186  		return errors[0]
   187  	} else if len(errors) > 1 {
   188  		// Aggregate errors
   189  		return fmt.Errorf("Multiple errors on shutdown: %v", errors)
   190  	}
   191  	return nil
   192  }
   193  
   194  // PushConnectionStatusChange pushes human readable connection status changes.
   195  func (fs *KBFSOpsStandard) PushConnectionStatusChange(
   196  	service string, newStatus error) {
   197  	fs.currentStatus.PushConnectionStatusChange(service, newStatus)
   198  
   199  	if service == MDServiceName {
   200  		fs.config.SubscriptionManagerPublisher().PublishChange(
   201  			keybase1.SubscriptionTopic_FILES_TAB_BADGE)
   202  	}
   203  
   204  	if fs.config.KeybaseService() == nil {
   205  		return
   206  	}
   207  
   208  	switch service {
   209  	case KeybaseServiceName, GregorServiceName:
   210  	default:
   211  		return
   212  	}
   213  
   214  	fs.opsLock.Lock()
   215  	defer fs.opsLock.Unlock()
   216  
   217  	for _, fbo := range fs.ops {
   218  		fbo.PushConnectionStatusChange(service, newStatus)
   219  	}
   220  
   221  	if newStatus == nil {
   222  		fs.log.CDebugf(
   223  			context.TODO(), "Asking for an edit re-init after reconnection")
   224  		fs.editActivity.Add(1)
   225  		go fs.initTlfsForEditHistories()
   226  		go fs.initSyncedTlfs()
   227  	}
   228  }
   229  
   230  // PushStatusChange forces a new status be fetched by status listeners.
   231  func (fs *KBFSOpsStandard) PushStatusChange() {
   232  	fs.currentStatus.PushStatusChange()
   233  
   234  	fs.log.CDebugf(
   235  		context.TODO(), "Asking for an edit re-init after status change")
   236  	fs.editActivity.Add(1)
   237  	go fs.initTlfsForEditHistories()
   238  	go fs.initSyncedTlfs()
   239  }
   240  
   241  // ClearPrivateFolderMD implements the KBFSOps interface for
   242  // KBFSOpsStandard.
   243  func (fs *KBFSOpsStandard) ClearPrivateFolderMD(ctx context.Context) {
   244  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   245  	defer timeTrackerDone()
   246  
   247  	fs.opsLock.Lock()
   248  	defer fs.opsLock.Unlock()
   249  
   250  	// Block until all private folders have been reset.  TODO:
   251  	// parallelize these, as they can block for a while waiting for
   252  	// the lock.
   253  	for _, fbo := range fs.ops {
   254  		// This call is a no-op for public folders.
   255  		fbo.ClearPrivateFolderMD(ctx)
   256  	}
   257  }
   258  
   259  // ForceFastForward implements the KBFSOps interface for
   260  // KBFSOpsStandard.
   261  func (fs *KBFSOpsStandard) ForceFastForward(ctx context.Context) {
   262  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   263  	defer timeTrackerDone()
   264  
   265  	fs.opsLock.Lock()
   266  	defer fs.opsLock.Unlock()
   267  
   268  	fs.log.CDebugf(ctx, "Forcing fast-forwards for %d folders", len(fs.ops))
   269  	for _, fbo := range fs.ops {
   270  		fbo.ForceFastForward(ctx)
   271  	}
   272  }
   273  
   274  // InvalidateNodeAndChildren implements the KBFSOps interface for
   275  // KBFSOpsStandard.
   276  func (fs *KBFSOpsStandard) InvalidateNodeAndChildren(
   277  	ctx context.Context, node Node) error {
   278  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   279  	defer timeTrackerDone()
   280  
   281  	ops := fs.getOpsByNode(ctx, node)
   282  	return ops.InvalidateNodeAndChildren(ctx, node)
   283  }
   284  
   285  func (fs *KBFSOpsStandard) waitForEditHistoryInitialization(
   286  	ctx context.Context) {
   287  	if !fs.config.Mode().TLFEditHistoryEnabled() ||
   288  		ctx.Value(ctxKBFSOpsSkipEditHistoryBlock) != nil {
   289  		return
   290  	}
   291  
   292  	reqChan, doneChan := func() (chan<- struct{}, <-chan struct{}) {
   293  		fs.initLock.Lock()
   294  		defer fs.initLock.Unlock()
   295  		return fs.initEditReq, fs.initEditDone
   296  	}()
   297  	// There hasn't been a logged-in event yet, so don't wait for the
   298  	// edit history to be initialized.
   299  	if reqChan == nil {
   300  		return
   301  	}
   302  
   303  	// Send a request to unblock, if needed.
   304  	select {
   305  	case reqChan <- struct{}{}:
   306  		fs.config.GetPerfLog().CDebugf(
   307  			ctx, "reqChan unblock waitForEditHistoryInitialization")
   308  	default:
   309  	}
   310  
   311  	select {
   312  	case <-doneChan:
   313  		// Avoid printing the log message if we don't need to wait at all.
   314  		return
   315  	default:
   316  	}
   317  
   318  	fs.log.CDebugf(ctx, "Waiting for the edit history to initialize")
   319  
   320  	// Don't wait too long for the edit history to be initialized, in
   321  	// case we are offline and someone is just trying to get the
   322  	// cached favorites.
   323  	ctx, cancel := context.WithTimeout(ctx, initEditBlockTimeout)
   324  	defer cancel()
   325  
   326  	// Wait for the initialization to complete.
   327  	select {
   328  	case <-doneChan:
   329  	case <-ctx.Done():
   330  	}
   331  }
   332  
   333  // GetFavorites implements the KBFSOps interface for
   334  // KBFSOpsStandard.
   335  func (fs *KBFSOpsStandard) GetFavorites(ctx context.Context) (
   336  	[]favorites.Folder, error) {
   337  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   338  	defer timeTrackerDone()
   339  
   340  	fs.waitForEditHistoryInitialization(ctx)
   341  
   342  	favs, err := fs.favs.Get(ctx)
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	// Add the conflict status for any folders in a conflict state to
   348  	// the favorite struct.
   349  	journalManager, err := GetJournalManager(fs.config)
   350  	if err != nil {
   351  		// Journaling not enabled.
   352  		return favs, nil
   353  	}
   354  	_, cleared, err := journalManager.GetJournalsInConflict(ctx)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	for _, c := range cleared {
   360  		favs = append(favs, favorites.Folder{
   361  			Name: string(c.Name),
   362  			Type: c.Type,
   363  		})
   364  	}
   365  	return favs, nil
   366  }
   367  
   368  func (fs *KBFSOpsStandard) getConflictMaps(ctx context.Context) (
   369  	conflictMap map[ConflictJournalRecord]tlf.ID,
   370  	clearedMap map[string][]keybase1.Path,
   371  	cleared []ConflictJournalRecord, err error) {
   372  	journalManager, err := GetJournalManager(fs.config)
   373  	if err != nil {
   374  		// Journaling not enabled.
   375  		return nil, nil, nil, nil
   376  	}
   377  	conflicts, cleared, err := journalManager.GetJournalsInConflict(ctx)
   378  	if err != nil {
   379  		return nil, nil, nil, err
   380  	}
   381  
   382  	if len(conflicts) == 0 && len(cleared) == 0 {
   383  		return nil, nil, nil, nil
   384  	}
   385  
   386  	clearedMap = make(map[string][]keybase1.Path)
   387  	for _, c := range cleared {
   388  		clearedMap[c.ServerViewPath.String()] = append(
   389  			clearedMap[c.ServerViewPath.String()], c.LocalViewPath)
   390  	}
   391  
   392  	conflictMap = make(map[ConflictJournalRecord]tlf.ID, len(conflicts))
   393  	for _, c := range conflicts {
   394  		conflictMap[ConflictJournalRecord{Name: c.Name, Type: c.Type}] = c.ID
   395  	}
   396  	return conflictMap, clearedMap, cleared, nil
   397  }
   398  
   399  func (fs *KBFSOpsStandard) findRelatedFolders(ctx context.Context,
   400  	conflictMap map[ConflictJournalRecord]tlf.ID,
   401  	clearedMap map[string][]keybase1.Path,
   402  	folderName string, folderType keybase1.FolderType) (
   403  	folderNormalView keybase1.FolderNormalView, found bool, err error) {
   404  	name := tlf.CanonicalName(folderName)
   405  	t := tlf.TypeFromFolderType(folderType)
   406  	c := ConflictJournalRecord{
   407  		Name: name,
   408  		Type: t,
   409  	}
   410  
   411  	// First check for any current automatically-resolving
   412  	// conflicts, and whether we're stuck.
   413  	tlfID, ok := conflictMap[c]
   414  	if ok {
   415  		fb := data.FolderBranch{Tlf: tlfID, Branch: data.MasterBranch}
   416  		ops := fs.getOps(ctx, fb, FavoritesOpNoChange)
   417  		s, err := ops.FolderConflictStatus(ctx)
   418  		if err != nil {
   419  			return keybase1.FolderNormalView{}, false, err
   420  		}
   421  		if s != keybase1.FolderConflictType_NONE {
   422  			folderNormalView.ResolvingConflict = true
   423  			folderNormalView.StuckInConflict =
   424  				s == keybase1.FolderConflictType_IN_CONFLICT_AND_STUCK
   425  			found = true
   426  		}
   427  	}
   428  	// Then check whether this favorite has any local conflict views.
   429  	p := tlfhandle.BuildProtocolPathForTlfName(t, name)
   430  	localViews, ok := clearedMap[p.String()]
   431  	if ok {
   432  		folderNormalView.LocalViews = localViews
   433  		found = true
   434  	}
   435  
   436  	return folderNormalView, found, nil
   437  }
   438  
   439  // GetFolderWithFavFlags implements the KBFSOps interface.
   440  func (fs *KBFSOpsStandard) GetFolderWithFavFlags(ctx context.Context, handle *tlfhandle.Handle) (keybase1.FolderWithFavFlags, error) {
   441  	favFolder := handle.ToFavorite()
   442  	folderWithFavFlags, err := fs.favs.GetFolderWithFavFlags(ctx, favFolder)
   443  	if err != nil {
   444  		return keybase1.FolderWithFavFlags{}, err
   445  	}
   446  	if folderWithFavFlags == nil {
   447  		folderWithFavFlags = &keybase1.FolderWithFavFlags{
   448  			Folder: favoriteToFolder(favFolder, handle.FavoriteData()),
   449  		}
   450  	}
   451  
   452  	// Add sync config.
   453  	if fs.config.IsSyncedTlf(handle.TlfID()) {
   454  		syncConfig, err := fs.GetSyncConfig(ctx, handle.TlfID())
   455  		if err != nil {
   456  			return keybase1.FolderWithFavFlags{}, err
   457  		}
   458  		folderWithFavFlags.Folder.SyncConfig = &syncConfig
   459  	}
   460  
   461  	// Add conflict state
   462  	conflictMap, clearedMap, _, err := fs.getConflictMaps(ctx)
   463  	if err != nil {
   464  		return keybase1.FolderWithFavFlags{}, err
   465  	}
   466  	folderNormalView, found, err := fs.findRelatedFolders(
   467  		ctx, conflictMap, clearedMap, favFolder.Name, favFolder.Type.FolderType())
   468  	if err != nil {
   469  		return keybase1.FolderWithFavFlags{}, err
   470  	}
   471  	if found {
   472  		conflictState :=
   473  			keybase1.NewConflictStateWithNormalview(folderNormalView)
   474  		folderWithFavFlags.Folder.ConflictState = &conflictState
   475  	}
   476  
   477  	return *folderWithFavFlags, nil
   478  }
   479  
   480  // GetFavoritesAll implements the KBFSOps interface for
   481  // KBFSOpsStandard.
   482  func (fs *KBFSOpsStandard) GetFavoritesAll(ctx context.Context) (
   483  	keybase1.FavoritesResult, error) {
   484  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   485  	defer timeTrackerDone()
   486  
   487  	fs.waitForEditHistoryInitialization(ctx)
   488  
   489  	favs, err := fs.favs.GetAll(ctx)
   490  	if err != nil {
   491  		return keybase1.FavoritesResult{}, err
   492  	}
   493  
   494  	tlfMDs := fs.GetAllSyncedTlfMDs(ctx)
   495  
   496  	// If we have any synced TLFs, create a quick-index map for
   497  	// favorites based on name+type.
   498  	type mapKey struct {
   499  		name string
   500  		t    keybase1.FolderType
   501  	}
   502  	var indexedFavs map[mapKey]int
   503  	if len(tlfMDs) > 0 {
   504  		indexedFavs = make(map[mapKey]int, len(favs.FavoriteFolders))
   505  		for i, fav := range favs.FavoriteFolders {
   506  			indexedFavs[mapKey{fav.Name, fav.FolderType}] = i
   507  		}
   508  	}
   509  
   510  	// Add the sync config mode to each favorite.
   511  	for id, md := range tlfMDs {
   512  		config, err := fs.GetSyncConfig(ctx, id)
   513  		if err != nil {
   514  			return keybase1.FavoritesResult{}, err
   515  		}
   516  
   517  		if config.Mode == keybase1.FolderSyncMode_DISABLED {
   518  			panic(fmt.Sprintf(
   519  				"Folder %s has sync unexpectedly disabled", id))
   520  		}
   521  
   522  		name := string(md.Handle.GetCanonicalName())
   523  		i, ok := indexedFavs[mapKey{name, id.Type().FolderType()}]
   524  		if ok {
   525  			favs.FavoriteFolders[i].SyncConfig = &config
   526  		}
   527  	}
   528  	for i, fav := range favs.FavoriteFolders {
   529  		if fav.SyncConfig != nil {
   530  			continue
   531  		}
   532  		favs.FavoriteFolders[i].SyncConfig = &keybase1.FolderSyncConfig{
   533  			Mode: keybase1.FolderSyncMode_DISABLED,
   534  		}
   535  	}
   536  
   537  	// Add the conflict status for any folders in a conflict state to
   538  	// the favorite struct.
   539  	conflictMap, clearedMap, cleared, err := fs.getConflictMaps(ctx)
   540  	if err != nil {
   541  		return keybase1.FavoritesResult{}, err
   542  	}
   543  
   544  	for _, c := range cleared {
   545  		cs := keybase1.NewConflictStateWithManualresolvinglocalview(
   546  			keybase1.FolderConflictManualResolvingLocalView{
   547  				NormalView: c.ServerViewPath,
   548  			})
   549  		favs.FavoriteFolders = append(favs.FavoriteFolders,
   550  			keybase1.Folder{
   551  				Name:          string(c.Name),
   552  				FolderType:    c.Type.FolderType(),
   553  				Private:       c.Type != tlf.Public,
   554  				ResetMembers:  []keybase1.User{},
   555  				ConflictState: &cs,
   556  			})
   557  	}
   558  
   559  	found := 0
   560  	for i, f := range favs.FavoriteFolders {
   561  		folderNormalView, currentFavFound, err := fs.findRelatedFolders(ctx, conflictMap, clearedMap, f.Name, f.FolderType)
   562  		if err != nil {
   563  			return keybase1.FavoritesResult{}, err
   564  		}
   565  
   566  		if currentFavFound {
   567  			conflictState :=
   568  				keybase1.NewConflictStateWithNormalview(folderNormalView)
   569  			favs.FavoriteFolders[i].ConflictState = &conflictState
   570  			found++
   571  		}
   572  
   573  		if found == len(conflictMap)+len(clearedMap) {
   574  			// Short-circuit the loop if we've already found all the conflicts.
   575  			break
   576  		}
   577  	}
   578  
   579  	return favs, nil
   580  }
   581  
   582  // GetBadge implements the KBFSOps interface for KBFSOpsStandard.
   583  func (fs *KBFSOpsStandard) GetBadge(ctx context.Context) (
   584  	keybase1.FilesTabBadge, error) {
   585  	journalManager, err := GetJournalManager(fs.config)
   586  	if err != nil {
   587  		// Journaling not enabled.
   588  		return keybase1.FilesTabBadge_NONE, nil
   589  	}
   590  
   591  	tlfsInConflict, numUploadingTlfs, err := journalManager.GetFoldersSummary()
   592  	if err != nil {
   593  		return keybase1.FilesTabBadge_NONE, err
   594  	}
   595  
   596  	for _, tlfID := range tlfsInConflict {
   597  		fb := data.FolderBranch{Tlf: tlfID, Branch: data.MasterBranch}
   598  		ops := fs.getOps(ctx, fb, FavoritesOpNoChange)
   599  		s, err := ops.FolderConflictStatus(ctx)
   600  		if err != nil {
   601  			return keybase1.FilesTabBadge_NONE, err
   602  		}
   603  		if s == keybase1.FolderConflictType_IN_CONFLICT_AND_STUCK {
   604  			return keybase1.FilesTabBadge_UPLOADING_STUCK, nil
   605  		}
   606  	}
   607  
   608  	if numUploadingTlfs == 0 {
   609  		return keybase1.FilesTabBadge_NONE, nil
   610  	}
   611  
   612  	serviceErrors, _ := fs.config.KBFSOps().StatusOfServices()
   613  	if serviceErrors[MDServiceName] != nil {
   614  		// Assume that if we're not connected to the mdserver,
   615  		// then data isn't uploading.  Technically it could still
   616  		// be uploading to the bserver, but that should be very
   617  		// rare.
   618  		return keybase1.FilesTabBadge_AWAITING_UPLOAD, nil
   619  	}
   620  
   621  	return keybase1.FilesTabBadge_UPLOADING, nil
   622  }
   623  
   624  // RefreshCachedFavorites implements the KBFSOps interface for
   625  // KBFSOpsStandard.
   626  func (fs *KBFSOpsStandard) RefreshCachedFavorites(ctx context.Context,
   627  	mode FavoritesRefreshMode) {
   628  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   629  	defer timeTrackerDone()
   630  
   631  	fs.favs.RefreshCache(ctx, mode)
   632  }
   633  
   634  // ClearCachedFavorites implements the KBFSOps interface for
   635  // KBFSOpsStandard.
   636  func (fs *KBFSOpsStandard) ClearCachedFavorites(ctx context.Context) {
   637  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   638  	defer timeTrackerDone()
   639  
   640  	fs.favs.ClearCache(ctx)
   641  }
   642  
   643  // AddFavorite implements the KBFSOps interface for KBFSOpsStandard.
   644  func (fs *KBFSOpsStandard) AddFavorite(ctx context.Context,
   645  	fav favorites.Folder, data favorites.Data) error {
   646  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   647  	defer timeTrackerDone()
   648  
   649  	kbpki := fs.config.KBPKI()
   650  	_, err := kbpki.GetCurrentSession(ctx)
   651  	isLoggedIn := err == nil
   652  
   653  	if isLoggedIn {
   654  		err := fs.favs.Add(ctx, favorites.ToAdd{
   655  			Folder:  fav,
   656  			Data:    data,
   657  			Created: false,
   658  		})
   659  		if err != nil {
   660  			return err
   661  		}
   662  	}
   663  
   664  	return nil
   665  }
   666  
   667  // SetFavoritesHomeTLFInfo implements the KBFSOps interface for KBFSOpsStandard.
   668  func (fs *KBFSOpsStandard) SetFavoritesHomeTLFInfo(ctx context.Context,
   669  	info homeTLFInfo) {
   670  	fs.favs.setHomeTLFInfo(ctx, info)
   671  }
   672  
   673  func (fs *KBFSOpsStandard) getOpsByFav(fav favorites.Folder) *folderBranchOps {
   674  	fs.opsLock.Lock()
   675  	defer fs.opsLock.Unlock()
   676  	return fs.opsByFav[fav]
   677  }
   678  
   679  // RefreshEditHistory implements the KBFSOps interface for KBFSOpsStandard
   680  func (fs *KBFSOpsStandard) RefreshEditHistory(fav favorites.Folder) {
   681  	fbo := fs.getOpsByFav(fav)
   682  	if fbo != nil {
   683  		fbo.refreshEditHistory()
   684  	}
   685  }
   686  
   687  // DeleteFavorite implements the KBFSOps interface for
   688  // KBFSOpsStandard.
   689  func (fs *KBFSOpsStandard) DeleteFavorite(ctx context.Context,
   690  	fav favorites.Folder) error {
   691  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
   692  	defer timeTrackerDone()
   693  
   694  	kbpki := fs.config.KBPKI()
   695  	_, err := kbpki.GetCurrentSession(ctx)
   696  	isLoggedIn := err == nil
   697  
   698  	// Let this ops remove itself, if we have one available.
   699  	ops := fs.getOpsByFav(fav)
   700  	if ops != nil {
   701  		err := ops.doFavoritesOp(ctx, FavoritesOpRemove, nil)
   702  		if _, ok := err.(OpsCantHandleFavorite); !ok {
   703  			return err
   704  		}
   705  		// If the ops couldn't handle the delete, fall through to
   706  		// going directly via Favorites.
   707  	}
   708  
   709  	if isLoggedIn {
   710  		err := fs.favs.Delete(ctx, fav)
   711  		if err != nil {
   712  			return err
   713  		}
   714  	}
   715  
   716  	// TODO: Shut down the running folderBranchOps, if one exists?
   717  	// What about open file handles?
   718  
   719  	return nil
   720  }
   721  
   722  func (fs *KBFSOpsStandard) getOpsNoAdd(
   723  	ctx context.Context, fb data.FolderBranch) *folderBranchOps {
   724  	if fb == (data.FolderBranch{}) {
   725  		panic("zero FolderBranch in getOps")
   726  	}
   727  
   728  	fs.opsLock.RLock()
   729  	if ops, ok := fs.ops[fb]; ok {
   730  		fs.opsLock.RUnlock()
   731  		return ops
   732  	}
   733  
   734  	fs.opsLock.RUnlock()
   735  	fs.opsLock.Lock()
   736  	defer fs.opsLock.Unlock()
   737  	// look it up again in case someone else got the lock
   738  	ops, ok := fs.ops[fb]
   739  	if !ok {
   740  		bType := standard
   741  		if fb.Branch.IsArchived() {
   742  			bType = archive
   743  		} else if fb.Branch.IsLocalConflict() {
   744  			bType = conflict
   745  		}
   746  		ops = newFolderBranchOps(
   747  			ctx, fs.appStateUpdater, fs.config, fb, bType,
   748  			fs.currentStatus, fs.favs, fs.syncedTlfObservers)
   749  		fs.ops[fb] = ops
   750  	}
   751  	return ops
   752  }
   753  
   754  func (fs *KBFSOpsStandard) getOpsIfExists(
   755  	ctx context.Context, fb data.FolderBranch) *folderBranchOps {
   756  	if fb == (data.FolderBranch{}) {
   757  		panic("zero FolderBranch in getOps")
   758  	}
   759  
   760  	fs.opsLock.RLock()
   761  	defer fs.opsLock.RUnlock()
   762  	return fs.ops[fb]
   763  }
   764  
   765  func (fs *KBFSOpsStandard) getOps(ctx context.Context,
   766  	fb data.FolderBranch, fop FavoritesOp) *folderBranchOps {
   767  	ops := fs.getOpsNoAdd(ctx, fb)
   768  	if err := ops.doFavoritesOp(ctx, fop, nil); err != nil {
   769  		// Failure to favorite shouldn't cause a failure.  Just log
   770  		// and move on.
   771  		fs.log.CDebugf(ctx, "Couldn't add favorite: %v", err)
   772  	}
   773  	return ops
   774  }
   775  
   776  func (fs *KBFSOpsStandard) getOpsByNode(ctx context.Context,
   777  	node Node) *folderBranchOps {
   778  	return fs.getOps(ctx, node.GetFolderBranch(), FavoritesOpAdd)
   779  }
   780  
   781  func (fs *KBFSOpsStandard) getOpsByHandle(ctx context.Context,
   782  	handle *tlfhandle.Handle, fb data.FolderBranch, fop FavoritesOp) *folderBranchOps {
   783  	ops := fs.getOpsNoAdd(ctx, fb)
   784  	if err := ops.doFavoritesOp(ctx, fop, handle); err != nil {
   785  		// Failure to favorite shouldn't cause a failure.  Just log
   786  		// and move on.
   787  		fs.log.CDebugf(ctx, "Couldn't add favorite: %+v", err)
   788  	}
   789  
   790  	fs.opsLock.Lock()
   791  	defer fs.opsLock.Unlock()
   792  	fav := handle.ToFavorite()
   793  	_, ok := fs.opsByFav[fav]
   794  	if ok {
   795  		// Already added.
   796  		return ops
   797  	}
   798  
   799  	// Track under its name, so we can later tell it to remove itself
   800  	// from the favorites list.
   801  	fs.opsByFav[fav] = ops
   802  	err := ops.RegisterForChanges(&kbfsOpsFavoriteObserver{
   803  		kbfsOps: fs,
   804  		currFav: fav,
   805  	})
   806  	if err != nil {
   807  		fs.log.CDebugf(ctx, "Couldn't register for changes: %+v", err)
   808  	}
   809  	return ops
   810  }
   811  
   812  func (fs *KBFSOpsStandard) resetTlfID(
   813  	ctx context.Context, h *tlfhandle.Handle, newTlfID *tlf.ID) error {
   814  	if !h.IsBackedByTeam() {
   815  		return errors.WithStack(NonExistentTeamForHandleError{h})
   816  	}
   817  
   818  	teamID, err := h.FirstResolvedWriter().AsTeam()
   819  	if err != nil {
   820  		return err
   821  	}
   822  
   823  	var tlfID tlf.ID
   824  	if newTlfID != nil {
   825  		tlfID = *newTlfID
   826  		fs.log.CDebugf(ctx, "Resetting to TLF ID %s for TLF %s, %s",
   827  			tlfID, teamID, h.GetCanonicalName())
   828  	} else {
   829  		matches, epoch, err := h.TlfID().GetEpochFromTeamTLF(teamID)
   830  		if err != nil {
   831  			return err
   832  		}
   833  		if matches {
   834  			epoch++
   835  		} else {
   836  			epoch = 0
   837  		}
   838  
   839  		// When creating a new TLF for an implicit team, always start with
   840  		// epoch 0.  A different path will handle TLF resets with an
   841  		// increased epoch, if necessary.
   842  		tlfID, err = tlf.MakeIDFromTeam(h.Type(), teamID, epoch)
   843  		if err != nil {
   844  			return err
   845  		}
   846  
   847  		fs.log.CDebugf(ctx, "Creating new TLF ID %s for TLF %s, %s",
   848  			tlfID, teamID, h.GetCanonicalName())
   849  	}
   850  
   851  	err = fs.config.KBPKI().CreateTeamTLF(ctx, teamID, tlfID)
   852  	if err != nil {
   853  		return err
   854  	}
   855  
   856  	h.SetTlfID(tlfID)
   857  	return fs.config.MDCache().PutIDForHandle(h, tlfID)
   858  }
   859  
   860  // createAndStoreTlfIDIfNeeded creates a TLF ID for a team-backed
   861  // handle that doesn't have one yet, and associates it in the service
   862  // with the team.  If it returns a `nil` error, it may have modified
   863  // `h` to include the new TLF ID.
   864  func (fs *KBFSOpsStandard) createAndStoreTlfIDIfNeeded(
   865  	ctx context.Context, h *tlfhandle.Handle) error {
   866  	if h.TlfID() != tlf.NullID {
   867  		return nil
   868  	}
   869  
   870  	return fs.resetTlfID(ctx, h, nil)
   871  }
   872  
   873  func (fs *KBFSOpsStandard) transformReadError(
   874  	ctx context.Context, h *tlfhandle.Handle, err error) error {
   875  	if errors.Cause(err) != context.DeadlineExceeded {
   876  		return err
   877  	}
   878  	if _, ok := errors.Cause(err).(OfflineUnsyncedError); ok {
   879  		return err
   880  	}
   881  
   882  	if fs.config.IsSyncedTlf(h.TlfID()) {
   883  		fs.log.CWarningf(ctx, "Got a read timeout on a synced TLF: %+v", err)
   884  		return err
   885  	}
   886  
   887  	// For unsynced TLFs, return a specific error to let the system
   888  	// know to show a sync recommendation.
   889  	return errors.WithStack(OfflineUnsyncedError{h})
   890  }
   891  
   892  func (fs *KBFSOpsStandard) getOrInitializeNewMDMaster(ctx context.Context,
   893  	mdops MDOps, h *tlfhandle.Handle, fb data.FolderBranch, create bool, fop FavoritesOp) (
   894  	initialized bool, md ImmutableRootMetadata, id tlf.ID, err error) {
   895  	defer func() {
   896  		err = fs.transformReadError(ctx, h, err)
   897  		if tlfhandle.GetExtendedIdentify(ctx).Behavior.AlwaysRunIdentify() &&
   898  			!initialized && err == nil {
   899  			kbpki := fs.config.KBPKI()
   900  			// We are not running identify for existing TLFs in
   901  			// KBFS. This makes sure if requested, identify runs even
   902  			// for existing TLFs.
   903  			err = tlfhandle.IdentifyHandle(ctx, kbpki, kbpki, fs.config, h)
   904  		}
   905  	}()
   906  
   907  	err = fs.createAndStoreTlfIDIfNeeded(ctx, h)
   908  	if err != nil {
   909  		return false, ImmutableRootMetadata{}, tlf.NullID, err
   910  	}
   911  
   912  	if rev, isRevBranch := fb.Branch.RevisionIfSpecified(); isRevBranch {
   913  		fs.log.CDebugf(ctx, "Getting archived revision %d for branch %s",
   914  			rev, fb.Branch)
   915  
   916  		// Make sure that rev hasn't been garbage-collected yet.
   917  		rmd, err := fs.getMDByHandle(ctx, h, FavoritesOpNoChange)
   918  		if err != nil {
   919  			return false, ImmutableRootMetadata{}, tlf.NullID, err
   920  		}
   921  		if rmd != (ImmutableRootMetadata{}) && rmd.IsReadable() {
   922  			// `rev` is still readable even if it matches
   923  			// `rmd.data.LastGCRevision`, since the GC process just
   924  			// removes the unref'd blocks in that revision; the actual
   925  			// data represented by the revision is still readable.
   926  			if rev < rmd.data.LastGCRevision {
   927  				return false, ImmutableRootMetadata{}, tlf.NullID,
   928  					RevGarbageCollectedError{rev, rmd.data.LastGCRevision}
   929  			}
   930  		}
   931  
   932  		md, err = GetSingleMD(
   933  			ctx, fs.config, h.TlfID(), kbfsmd.NullBranchID, rev,
   934  			kbfsmd.Merged, nil)
   935  		// This will error if there's no corresponding MD, which is
   936  		// what we want since that means the user input an incorrect
   937  		// MD revision.
   938  		if err != nil {
   939  			return false, ImmutableRootMetadata{}, tlf.NullID, err
   940  		}
   941  		return false, md, h.TlfID(), nil
   942  	}
   943  
   944  	md, err = mdops.GetForTLF(ctx, h.TlfID(), nil)
   945  	if err != nil {
   946  		return false, ImmutableRootMetadata{}, tlf.NullID, err
   947  	}
   948  	if md != (ImmutableRootMetadata{}) {
   949  		return false, md, h.TlfID(), nil
   950  	}
   951  
   952  	if !create {
   953  		return false, ImmutableRootMetadata{}, h.TlfID(), nil
   954  	}
   955  
   956  	// Init new MD.
   957  	fops := fs.getOpsByHandle(ctx, h, fb, fop)
   958  	err = fops.SetInitialHeadToNew(ctx, h.TlfID(), h)
   959  	// Someone else initialized the TLF out from under us, so we
   960  	// didn't initialize it.
   961  	_, alreadyExisted := errors.Cause(err).(RekeyConflictError)
   962  	if err != nil && !alreadyExisted {
   963  		return false, ImmutableRootMetadata{}, tlf.NullID, err
   964  	}
   965  
   966  	md, err = mdops.GetForTLF(ctx, h.TlfID(), nil)
   967  	if err != nil {
   968  		return false, ImmutableRootMetadata{}, tlf.NullID, err
   969  	}
   970  
   971  	return !alreadyExisted, md, h.TlfID(), err
   972  
   973  }
   974  
   975  func (fs *KBFSOpsStandard) getMDByHandle(ctx context.Context,
   976  	tlfHandle *tlfhandle.Handle, fop FavoritesOp) (rmd ImmutableRootMetadata, err error) {
   977  	fbo := fs.getOpsByFav(tlfHandle.ToFavorite())
   978  	if fbo != nil {
   979  		lState := makeFBOLockState()
   980  		rmd, err = fbo.getMDForReadNeedIdentifyOnMaybeFirstAccess(ctx, lState)
   981  		if err != nil {
   982  			return ImmutableRootMetadata{}, err
   983  		}
   984  	}
   985  	if rmd != (ImmutableRootMetadata{}) {
   986  		return rmd, nil
   987  	}
   988  
   989  	err = fs.createAndStoreTlfIDIfNeeded(ctx, tlfHandle)
   990  	if err != nil {
   991  		return ImmutableRootMetadata{}, err
   992  	}
   993  
   994  	// Check for an unmerged MD first if necessary.
   995  	if fs.config.Mode().UnmergedTLFsEnabled() {
   996  		rmd, err = fs.config.MDOps().GetUnmergedForTLF(
   997  			ctx, tlfHandle.TlfID(), kbfsmd.NullBranchID)
   998  		if err != nil {
   999  			return ImmutableRootMetadata{}, err
  1000  		}
  1001  	}
  1002  
  1003  	fb := data.FolderBranch{Tlf: tlfHandle.TlfID(), Branch: data.MasterBranch}
  1004  	if rmd == (ImmutableRootMetadata{}) {
  1005  		if fop == FavoritesOpAdd {
  1006  			_, rmd, _, err = fs.getOrInitializeNewMDMaster(
  1007  				ctx, fs.config.MDOps(), tlfHandle, fb, true,
  1008  				FavoritesOpAddNewlyCreated)
  1009  		} else {
  1010  			_, rmd, _, err = fs.getOrInitializeNewMDMaster(
  1011  				ctx, fs.config.MDOps(), tlfHandle, fb, true, fop)
  1012  		}
  1013  		if err != nil {
  1014  			return ImmutableRootMetadata{}, err
  1015  		}
  1016  	}
  1017  
  1018  	// Make sure fbo exists and head is set so that next time we use this we
  1019  	// don't need to hit server even when there isn't any FS activity.
  1020  	if fbo == nil {
  1021  		fbo = fs.getOpsByHandle(ctx, tlfHandle, fb, fop)
  1022  	}
  1023  	if err = fbo.SetInitialHeadFromServer(ctx, rmd); err != nil {
  1024  		return ImmutableRootMetadata{}, err
  1025  	}
  1026  
  1027  	return rmd, nil
  1028  }
  1029  
  1030  // GetTLFCryptKeys implements the KBFSOps interface for
  1031  // KBFSOpsStandard
  1032  func (fs *KBFSOpsStandard) GetTLFCryptKeys(
  1033  	ctx context.Context, tlfHandle *tlfhandle.Handle) (
  1034  	keys []kbfscrypto.TLFCryptKey, id tlf.ID, err error) {
  1035  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1036  	defer timeTrackerDone()
  1037  
  1038  	fs.log.CDebugf(ctx, "GetTLFCryptKeys(%s)", tlfHandle.GetCanonicalPath())
  1039  	defer func() { fs.deferLog.CDebugf(ctx, "Done: %+v", err) }()
  1040  
  1041  	rmd, err := fs.getMDByHandle(ctx, tlfHandle, FavoritesOpNoChange)
  1042  	if err != nil {
  1043  		return nil, tlf.ID{}, err
  1044  	}
  1045  	keys, err = fs.config.KeyManager().GetTLFCryptKeyOfAllGenerations(ctx, rmd)
  1046  	return keys, rmd.TlfID(), err
  1047  }
  1048  
  1049  // GetTLFID implements the KBFSOps interface for KBFSOpsStandard.
  1050  func (fs *KBFSOpsStandard) GetTLFID(ctx context.Context,
  1051  	tlfHandle *tlfhandle.Handle) (id tlf.ID, err error) {
  1052  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1053  	defer timeTrackerDone()
  1054  
  1055  	fs.log.CDebugf(ctx, "GetTLFID(%s)", tlfHandle.GetCanonicalPath())
  1056  	defer func() { fs.deferLog.CDebugf(ctx, "Done: %+v", err) }()
  1057  
  1058  	rmd, err := fs.getMDByHandle(ctx, tlfHandle, FavoritesOpNoChange)
  1059  	if err != nil {
  1060  		return tlf.ID{}, err
  1061  	}
  1062  	return rmd.TlfID(), err
  1063  }
  1064  
  1065  // GetTLFHandle implements the KBFSOps interface for KBFSOpsStandard.
  1066  func (fs *KBFSOpsStandard) GetTLFHandle(ctx context.Context, node Node) (
  1067  	*tlfhandle.Handle, error) {
  1068  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1069  	defer timeTrackerDone()
  1070  
  1071  	ops := fs.getOpsByNode(ctx, node)
  1072  	return ops.GetTLFHandle(ctx, node)
  1073  }
  1074  
  1075  // getMaybeCreateRootNode is called for GetOrCreateRootNode and GetRootNode.
  1076  func (fs *KBFSOpsStandard) getMaybeCreateRootNode(
  1077  	ctx context.Context, h *tlfhandle.Handle, branch data.BranchName, create bool) (
  1078  	node Node, ei data.EntryInfo, err error) {
  1079  	fs.log.CDebugf(ctx, "getMaybeCreateRootNode(%s, %v, %v)",
  1080  		h.GetCanonicalPath(), branch, create)
  1081  	defer func() {
  1082  		err = fs.transformReadError(ctx, h, err)
  1083  		fs.deferLog.CDebugf(ctx, "Done: %+v", err)
  1084  	}()
  1085  
  1086  	if branch != data.MasterBranch && create {
  1087  		return nil, data.EntryInfo{}, errors.Errorf(
  1088  			"Can't create a root node for branch %s", branch)
  1089  	}
  1090  
  1091  	err = fs.createAndStoreTlfIDIfNeeded(ctx, h)
  1092  	if err != nil {
  1093  		return nil, data.EntryInfo{}, err
  1094  	}
  1095  
  1096  	// Check if we already have the MD cached, before contacting any
  1097  	// servers.
  1098  	if h.TlfID() == tlf.NullID {
  1099  		return nil, data.EntryInfo{},
  1100  			errors.Errorf("Handle for %s doesn't have a TLF ID set",
  1101  				h.GetCanonicalPath())
  1102  	}
  1103  	fb := data.FolderBranch{Tlf: h.TlfID(), Branch: branch}
  1104  	fops := fs.getOpsIfExists(ctx, fb)
  1105  	if fops != nil {
  1106  		// If a folderBranchOps has already been initialized for this TLF,
  1107  		// use it to get the root node.  But if we haven't done an
  1108  		// identify yet, we better do so, because `getRootNode()` doesn't
  1109  		// do one.
  1110  		lState := makeFBOLockState()
  1111  		md, err := fops.getMDForReadNeedIdentifyOnMaybeFirstAccess(ctx, lState)
  1112  		if err != nil {
  1113  			return nil, data.EntryInfo{}, err
  1114  		}
  1115  		if md != (ImmutableRootMetadata{}) && md.IsReadable() {
  1116  			node, ei, _, err := fops.getRootNode(ctx)
  1117  			if err != nil {
  1118  				return nil, data.EntryInfo{}, err
  1119  			}
  1120  			if node != nil {
  1121  				return node, ei, nil
  1122  			}
  1123  		}
  1124  	}
  1125  
  1126  	mdops := fs.config.MDOps()
  1127  	var md ImmutableRootMetadata
  1128  	// Check for an unmerged MD first if necessary.
  1129  	if fs.config.Mode().UnmergedTLFsEnabled() {
  1130  		md, err = mdops.GetUnmergedForTLF(ctx, h.TlfID(), kbfsmd.NullBranchID)
  1131  		if err != nil {
  1132  			return nil, data.EntryInfo{}, err
  1133  		}
  1134  	}
  1135  
  1136  	if md == (ImmutableRootMetadata{}) {
  1137  		var id tlf.ID
  1138  		var initialized bool
  1139  		initialized, md, id, err = fs.getOrInitializeNewMDMaster(
  1140  			ctx, mdops, h, fb, create, FavoritesOpAdd)
  1141  		if err != nil {
  1142  			return nil, data.EntryInfo{}, err
  1143  		}
  1144  		if initialized {
  1145  			fb := data.FolderBranch{Tlf: id, Branch: data.MasterBranch}
  1146  			fops := fs.getOpsByHandle(ctx, h, fb, FavoritesOpAddNewlyCreated)
  1147  
  1148  			node, ei, _, err = fops.getRootNode(ctx)
  1149  			if err != nil {
  1150  				return nil, data.EntryInfo{}, err
  1151  			}
  1152  
  1153  			return node, ei, nil
  1154  		}
  1155  		if !create && md == (ImmutableRootMetadata{}) {
  1156  			kbpki := fs.config.KBPKI()
  1157  			err := tlfhandle.IdentifyHandle(ctx, kbpki, kbpki, fs.config, h)
  1158  			if err != nil {
  1159  				return nil, data.EntryInfo{}, err
  1160  			}
  1161  			fb := data.FolderBranch{Tlf: id, Branch: data.MasterBranch}
  1162  			fs.getOpsByHandle(ctx, h, fb, FavoritesOpAdd)
  1163  			return nil, data.EntryInfo{}, nil
  1164  		}
  1165  	}
  1166  
  1167  	// we might not be able to read the metadata if we aren't in the
  1168  	// key group yet.
  1169  	if err := isReadableOrError(ctx, fs.config.KBPKI(), fs.config, md.ReadOnly()); err != nil {
  1170  		fs.opsLock.Lock()
  1171  		defer fs.opsLock.Unlock()
  1172  		// If we already have an FBO for this ID, trigger a rekey
  1173  		// prompt in the background, if possible.
  1174  		if ops, ok := fs.ops[fb]; ok {
  1175  			fs.log.CDebugf(ctx, "Triggering a paper prompt rekey on folder "+
  1176  				"access due to unreadable MD for %s", h.GetCanonicalPath())
  1177  			ops.rekeyFSM.Event(NewRekeyRequestWithPaperPromptEvent())
  1178  		}
  1179  		return nil, data.EntryInfo{}, err
  1180  	}
  1181  
  1182  	ops := fs.getOpsByHandle(ctx, h, fb, FavoritesOpAdd)
  1183  
  1184  	err = ops.SetInitialHeadFromServer(ctx, md)
  1185  	if err != nil {
  1186  		return nil, data.EntryInfo{}, err
  1187  	}
  1188  
  1189  	node, ei, _, err = ops.getRootNode(ctx)
  1190  	if err != nil {
  1191  		return nil, data.EntryInfo{}, err
  1192  	}
  1193  
  1194  	if err := ops.doFavoritesOp(ctx, FavoritesOpAdd, h); err != nil {
  1195  		// Failure to favorite shouldn't cause a failure.  Just log
  1196  		// and move on.
  1197  		fs.log.CDebugf(ctx, "Couldn't add favorite: %v", err)
  1198  	}
  1199  	return node, ei, nil
  1200  }
  1201  
  1202  // GetOrCreateRootNode implements the KBFSOps interface for
  1203  // KBFSOpsStandard
  1204  func (fs *KBFSOpsStandard) GetOrCreateRootNode(
  1205  	ctx context.Context, h *tlfhandle.Handle, branch data.BranchName) (
  1206  	node Node, ei data.EntryInfo, err error) {
  1207  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1208  	defer timeTrackerDone()
  1209  
  1210  	return fs.getMaybeCreateRootNode(ctx, h, branch, true)
  1211  }
  1212  
  1213  // GetRootNode implements the KBFSOps interface for
  1214  // KBFSOpsStandard. Returns a nil Node and nil error
  1215  // if the tlf does not exist but there is no error present.
  1216  func (fs *KBFSOpsStandard) GetRootNode(
  1217  	ctx context.Context, h *tlfhandle.Handle, branch data.BranchName) (
  1218  	node Node, ei data.EntryInfo, err error) {
  1219  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1220  	defer timeTrackerDone()
  1221  
  1222  	return fs.getMaybeCreateRootNode(ctx, h, branch, false)
  1223  }
  1224  
  1225  // GetDirChildren implements the KBFSOps interface for KBFSOpsStandard
  1226  func (fs *KBFSOpsStandard) GetDirChildren(ctx context.Context, dir Node) (
  1227  	map[data.PathPartString]data.EntryInfo, error) {
  1228  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1229  	defer timeTrackerDone()
  1230  
  1231  	ops := fs.getOpsByNode(ctx, dir)
  1232  	return ops.GetDirChildren(ctx, dir)
  1233  }
  1234  
  1235  // Lookup implements the KBFSOps interface for KBFSOpsStandard
  1236  func (fs *KBFSOpsStandard) Lookup(
  1237  	ctx context.Context, dir Node, name data.PathPartString) (
  1238  	Node, data.EntryInfo, error) {
  1239  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1240  	defer timeTrackerDone()
  1241  
  1242  	ops := fs.getOpsByNode(ctx, dir)
  1243  	return ops.Lookup(ctx, dir, name)
  1244  }
  1245  
  1246  // Stat implements the KBFSOps interface for KBFSOpsStandard
  1247  func (fs *KBFSOpsStandard) Stat(ctx context.Context, node Node) (
  1248  	data.EntryInfo, error) {
  1249  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1250  	defer timeTrackerDone()
  1251  
  1252  	ops := fs.getOpsByNode(ctx, node)
  1253  	return ops.Stat(ctx, node)
  1254  }
  1255  
  1256  // CreateDir implements the KBFSOps interface for KBFSOpsStandard
  1257  func (fs *KBFSOpsStandard) CreateDir(
  1258  	ctx context.Context, dir Node, name data.PathPartString) (
  1259  	Node, data.EntryInfo, error) {
  1260  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1261  	defer timeTrackerDone()
  1262  
  1263  	ops := fs.getOpsByNode(ctx, dir)
  1264  	return ops.CreateDir(ctx, dir, name)
  1265  }
  1266  
  1267  // CreateFile implements the KBFSOps interface for KBFSOpsStandard
  1268  func (fs *KBFSOpsStandard) CreateFile(
  1269  	ctx context.Context, dir Node, name data.PathPartString, isExec bool,
  1270  	excl Excl) (Node, data.EntryInfo, error) {
  1271  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1272  	defer timeTrackerDone()
  1273  
  1274  	ops := fs.getOpsByNode(ctx, dir)
  1275  	return ops.CreateFile(ctx, dir, name, isExec, excl)
  1276  }
  1277  
  1278  // CreateLink implements the KBFSOps interface for KBFSOpsStandard
  1279  func (fs *KBFSOpsStandard) CreateLink(
  1280  	ctx context.Context, dir Node, fromName, toPath data.PathPartString) (
  1281  	data.EntryInfo, error) {
  1282  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1283  	defer timeTrackerDone()
  1284  
  1285  	ops := fs.getOpsByNode(ctx, dir)
  1286  	return ops.CreateLink(ctx, dir, fromName, toPath)
  1287  }
  1288  
  1289  // RemoveDir implements the KBFSOps interface for KBFSOpsStandard
  1290  func (fs *KBFSOpsStandard) RemoveDir(
  1291  	ctx context.Context, dir Node, name data.PathPartString) error {
  1292  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1293  	defer timeTrackerDone()
  1294  
  1295  	ops := fs.getOpsByNode(ctx, dir)
  1296  	return ops.RemoveDir(ctx, dir, name)
  1297  }
  1298  
  1299  // RemoveEntry implements the KBFSOps interface for KBFSOpsStandard
  1300  func (fs *KBFSOpsStandard) RemoveEntry(
  1301  	ctx context.Context, dir Node, name data.PathPartString) error {
  1302  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1303  	defer timeTrackerDone()
  1304  
  1305  	ops := fs.getOpsByNode(ctx, dir)
  1306  	return ops.RemoveEntry(ctx, dir, name)
  1307  }
  1308  
  1309  // Rename implements the KBFSOps interface for KBFSOpsStandard
  1310  func (fs *KBFSOpsStandard) Rename(
  1311  	ctx context.Context, oldParent Node, oldName data.PathPartString,
  1312  	newParent Node, newName data.PathPartString) error {
  1313  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1314  	defer timeTrackerDone()
  1315  
  1316  	oldFB := oldParent.GetFolderBranch()
  1317  	newFB := newParent.GetFolderBranch()
  1318  
  1319  	// only works for nodes within the same topdir
  1320  	if oldFB != newFB {
  1321  		return RenameAcrossDirsError{}
  1322  	}
  1323  
  1324  	ops := fs.getOpsByNode(ctx, oldParent)
  1325  	return ops.Rename(ctx, oldParent, oldName, newParent, newName)
  1326  }
  1327  
  1328  // Read implements the KBFSOps interface for KBFSOpsStandard
  1329  func (fs *KBFSOpsStandard) Read(
  1330  	ctx context.Context, file Node, dest []byte, off int64) (
  1331  	numRead int64, err error) {
  1332  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1333  	defer timeTrackerDone()
  1334  
  1335  	ops := fs.getOpsByNode(ctx, file)
  1336  	return ops.Read(ctx, file, dest, off)
  1337  }
  1338  
  1339  // Write implements the KBFSOps interface for KBFSOpsStandard
  1340  func (fs *KBFSOpsStandard) Write(
  1341  	ctx context.Context, file Node, data []byte, off int64) error {
  1342  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1343  	defer timeTrackerDone()
  1344  
  1345  	ops := fs.getOpsByNode(ctx, file)
  1346  	return ops.Write(ctx, file, data, off)
  1347  }
  1348  
  1349  // Truncate implements the KBFSOps interface for KBFSOpsStandard
  1350  func (fs *KBFSOpsStandard) Truncate(
  1351  	ctx context.Context, file Node, size uint64) error {
  1352  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1353  	defer timeTrackerDone()
  1354  
  1355  	ops := fs.getOpsByNode(ctx, file)
  1356  	return ops.Truncate(ctx, file, size)
  1357  }
  1358  
  1359  // SetEx implements the KBFSOps interface for KBFSOpsStandard
  1360  func (fs *KBFSOpsStandard) SetEx(
  1361  	ctx context.Context, file Node, ex bool) error {
  1362  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1363  	defer timeTrackerDone()
  1364  
  1365  	ops := fs.getOpsByNode(ctx, file)
  1366  	return ops.SetEx(ctx, file, ex)
  1367  }
  1368  
  1369  // SetMtime implements the KBFSOps interface for KBFSOpsStandard
  1370  func (fs *KBFSOpsStandard) SetMtime(
  1371  	ctx context.Context, file Node, mtime *time.Time) error {
  1372  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1373  	defer timeTrackerDone()
  1374  
  1375  	ops := fs.getOpsByNode(ctx, file)
  1376  	return ops.SetMtime(ctx, file, mtime)
  1377  }
  1378  
  1379  // SyncAll implements the KBFSOps interface for KBFSOpsStandard
  1380  func (fs *KBFSOpsStandard) SyncAll(
  1381  	ctx context.Context, folderBranch data.FolderBranch) error {
  1382  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1383  	defer timeTrackerDone()
  1384  
  1385  	ops := fs.getOps(ctx, folderBranch, FavoritesOpAdd)
  1386  	return ops.SyncAll(ctx, folderBranch)
  1387  }
  1388  
  1389  // FolderStatus implements the KBFSOps interface for KBFSOpsStandard
  1390  func (fs *KBFSOpsStandard) FolderStatus(
  1391  	ctx context.Context, folderBranch data.FolderBranch) (
  1392  	FolderBranchStatus, <-chan StatusUpdate, error) {
  1393  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1394  	defer timeTrackerDone()
  1395  
  1396  	ops := fs.getOps(ctx, folderBranch, FavoritesOpNoChange)
  1397  	return ops.FolderStatus(ctx, folderBranch)
  1398  }
  1399  
  1400  // FolderConflictStatus implements the KBFSOps interface for
  1401  // KBFSOpsStandard
  1402  func (fs *KBFSOpsStandard) FolderConflictStatus(
  1403  	ctx context.Context, folderBranch data.FolderBranch) (
  1404  	keybase1.FolderConflictType, error) {
  1405  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1406  	defer timeTrackerDone()
  1407  
  1408  	ops := fs.getOps(ctx, folderBranch, FavoritesOpNoChange)
  1409  	return ops.FolderConflictStatus(ctx)
  1410  }
  1411  
  1412  // Status implements the KBFSOps interface for KBFSOpsStandard
  1413  func (fs *KBFSOpsStandard) Status(ctx context.Context) (
  1414  	KBFSStatus, <-chan StatusUpdate, error) {
  1415  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1416  	defer timeTrackerDone()
  1417  
  1418  	session, err := fs.config.KBPKI().GetCurrentSession(ctx)
  1419  	var usageBytes, archiveBytes, limitBytes int64 = -1, -1, -1
  1420  	var gitUsageBytes, gitArchiveBytes, gitLimitBytes int64 = -1, -1, -1
  1421  	// Don't request the quota info until we're sure we've
  1422  	// authenticated with our password.  TODO: fix this in the
  1423  	// service/GUI by handling multiple simultaneous passphrase
  1424  	// requests at once.
  1425  	mdserver := fs.config.MDServer()
  1426  	switch errors.Cause(err).(type) {
  1427  	case nil:
  1428  		if mdserver != nil && mdserver.IsConnected() {
  1429  			var quErr error
  1430  			uid := session.UID.AsUserOrTeam()
  1431  			_, usageBytes, archiveBytes, limitBytes,
  1432  				gitUsageBytes, gitArchiveBytes, gitLimitBytes, quErr =
  1433  				fs.config.GetQuotaUsage(uid).GetAllTypes(
  1434  					ctx, quotaUsageStaleTolerance/2, quotaUsageStaleTolerance)
  1435  			if quErr != nil {
  1436  				// The error is ignored here so that other fields can still be populated
  1437  				// even if this fails.
  1438  				fs.log.CDebugf(ctx, "Getting quota usage error: %v", quErr)
  1439  			}
  1440  		} else {
  1441  			fs.log.CDebugf(ctx, "Skipping getting quota usage because "+
  1442  				"mdserver not set or not connected")
  1443  		}
  1444  	case idutil.NoCurrentSessionError:
  1445  		fs.log.CDebugf(ctx, "Skipping getting quota usage because "+
  1446  			"we are not logged in")
  1447  		err = nil
  1448  	default:
  1449  		return KBFSStatus{}, nil, err
  1450  	}
  1451  
  1452  	failures, ch := fs.currentStatus.CurrentStatus()
  1453  	var jManagerStatus *JournalManagerStatus
  1454  	jManager, jErr := GetJournalManager(fs.config)
  1455  	if jErr == nil {
  1456  		status, tlfIDs := jManager.Status(ctx)
  1457  		jManagerStatus = &status
  1458  		err := FillInJournalStatusUnflushedPaths(
  1459  			ctx, fs.config, jManagerStatus, tlfIDs)
  1460  		if err != nil {
  1461  			// The caller might depend on the channel (e.g., in
  1462  			// libfs/remote_status.go), even in the case where err !=
  1463  			// nil.
  1464  			return KBFSStatus{}, ch, err
  1465  		}
  1466  		if usageBytes >= 0 {
  1467  			usageBytes += status.UnflushedBytes
  1468  		}
  1469  	}
  1470  
  1471  	dbc := fs.config.DiskBlockCache()
  1472  	var dbcStatus map[string]DiskBlockCacheStatus
  1473  	if dbc != nil {
  1474  		dbcStatus = dbc.Status(ctx)
  1475  	}
  1476  
  1477  	dmc := fs.config.DiskMDCache()
  1478  	var dmcStatus DiskMDCacheStatus
  1479  	if dmc != nil {
  1480  		dmcStatus = dmc.Status(ctx)
  1481  	}
  1482  	dqc := fs.config.DiskQuotaCache()
  1483  	var dqcStatus DiskQuotaCacheStatus
  1484  	if dqc != nil {
  1485  		dqcStatus = dqc.Status(ctx)
  1486  	}
  1487  
  1488  	return KBFSStatus{
  1489  		CurrentUser:          session.Name.String(),
  1490  		IsConnected:          fs.config.MDServer().IsConnected(),
  1491  		UsageBytes:           usageBytes,
  1492  		ArchiveBytes:         archiveBytes,
  1493  		LimitBytes:           limitBytes,
  1494  		GitUsageBytes:        gitUsageBytes,
  1495  		GitArchiveBytes:      gitArchiveBytes,
  1496  		GitLimitBytes:        gitLimitBytes,
  1497  		FailingServices:      failures,
  1498  		JournalManager:       jManagerStatus,
  1499  		DiskBlockCacheStatus: dbcStatus,
  1500  		DiskMDCacheStatus:    dmcStatus,
  1501  		DiskQuotaCacheStatus: dqcStatus,
  1502  	}, ch, err
  1503  }
  1504  
  1505  // UnstageForTesting implements the KBFSOps interface for KBFSOpsStandard
  1506  // TODO: remove once we have automatic conflict resolution
  1507  func (fs *KBFSOpsStandard) UnstageForTesting(
  1508  	ctx context.Context, folderBranch data.FolderBranch) error {
  1509  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1510  	defer timeTrackerDone()
  1511  
  1512  	ops := fs.getOps(ctx, folderBranch, FavoritesOpAdd)
  1513  	return ops.UnstageForTesting(ctx, folderBranch)
  1514  }
  1515  
  1516  // RequestRekey implements the KBFSOps interface for KBFSOpsStandard
  1517  func (fs *KBFSOpsStandard) RequestRekey(ctx context.Context, id tlf.ID) {
  1518  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1519  	defer timeTrackerDone()
  1520  
  1521  	// We currently only support rekeys of master branches.
  1522  	ops := fs.getOps(ctx,
  1523  		data.FolderBranch{Tlf: id, Branch: data.MasterBranch}, FavoritesOpNoChange)
  1524  	ops.RequestRekey(ctx, id)
  1525  }
  1526  
  1527  // SyncFromServer implements the KBFSOps interface for KBFSOpsStandard
  1528  func (fs *KBFSOpsStandard) SyncFromServer(ctx context.Context,
  1529  	folderBranch data.FolderBranch, lockBeforeGet *keybase1.LockID) error {
  1530  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1531  	defer timeTrackerDone()
  1532  
  1533  	ops := fs.getOps(ctx, folderBranch, FavoritesOpAdd)
  1534  	return ops.SyncFromServer(ctx, folderBranch, lockBeforeGet)
  1535  }
  1536  
  1537  // GetUpdateHistory implements the KBFSOps interface for KBFSOpsStandard
  1538  func (fs *KBFSOpsStandard) GetUpdateHistory(
  1539  	ctx context.Context, folderBranch data.FolderBranch,
  1540  	start, end kbfsmd.Revision) (history TLFUpdateHistory, err error) {
  1541  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1542  	defer timeTrackerDone()
  1543  
  1544  	ops := fs.getOps(ctx, folderBranch, FavoritesOpAdd)
  1545  	return ops.GetUpdateHistory(ctx, folderBranch, start, end)
  1546  }
  1547  
  1548  // GetEditHistory implements the KBFSOps interface for KBFSOpsStandard
  1549  func (fs *KBFSOpsStandard) GetEditHistory(
  1550  	ctx context.Context, folderBranch data.FolderBranch) (
  1551  	tlfHistory keybase1.FSFolderEditHistory, err error) {
  1552  	ops := fs.getOps(ctx, folderBranch, FavoritesOpAdd)
  1553  	return ops.GetEditHistory(ctx, folderBranch)
  1554  }
  1555  
  1556  // GetNodeMetadata implements the KBFSOps interface for KBFSOpsStandard
  1557  func (fs *KBFSOpsStandard) GetNodeMetadata(ctx context.Context, node Node) (
  1558  	NodeMetadata, error) {
  1559  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1560  	defer timeTrackerDone()
  1561  
  1562  	ops := fs.getOpsByNode(ctx, node)
  1563  	return ops.GetNodeMetadata(ctx, node)
  1564  }
  1565  
  1566  // GetRootNodeMetadata implements the KBFSOps interface for KBFSOpsStandard
  1567  func (fs *KBFSOpsStandard) GetRootNodeMetadata(
  1568  	ctx context.Context, folderBranch data.FolderBranch) (
  1569  	NodeMetadata, *tlfhandle.Handle, error) {
  1570  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1571  	defer timeTrackerDone()
  1572  
  1573  	ops := fs.getOps(ctx, folderBranch, FavoritesOpNoChange)
  1574  	rootNode, _, _, err := ops.getRootNode(ctx)
  1575  	if err != nil {
  1576  		return NodeMetadata{}, nil, err
  1577  	}
  1578  	md, err := ops.GetNodeMetadata(ctx, rootNode)
  1579  	if err != nil {
  1580  		return NodeMetadata{}, nil, err
  1581  	}
  1582  
  1583  	h, err := ops.GetTLFHandle(ctx, rootNode)
  1584  	if err != nil {
  1585  		return NodeMetadata{}, nil, err
  1586  	}
  1587  	return md, h, nil
  1588  }
  1589  
  1590  func (fs *KBFSOpsStandard) findTeamByID(
  1591  	ctx context.Context, tid keybase1.TeamID) *folderBranchOps {
  1592  	fs.opsLock.Lock()
  1593  	// Copy the ops list so we don't have to hold opsLock when calling
  1594  	// `getRootNode()` (which can lead to deadlocks).
  1595  	ops := make(map[data.FolderBranch]*folderBranchOps)
  1596  	for fb, fbo := range fs.ops {
  1597  		ops[fb] = fbo
  1598  	}
  1599  	fs.opsLock.Unlock()
  1600  
  1601  	// We have to search for the tid since we don't know the old name
  1602  	// of the team here.  Should we add an index for this?
  1603  	for fb, fbo := range ops {
  1604  		_, _, handle, err := fbo.getRootNode(ctx)
  1605  		if err != nil {
  1606  			fs.log.CDebugf(
  1607  				ctx, "Error getting root node for %s: %+v", fb.Tlf, err)
  1608  			continue
  1609  		}
  1610  
  1611  		if handle.TypeForKeying() != tlf.TeamKeying {
  1612  			continue
  1613  		}
  1614  
  1615  		if handle.FirstResolvedWriter().AsTeamOrBust() != tid {
  1616  			continue
  1617  		}
  1618  
  1619  		fs.log.CDebugf(ctx, "Team name changed for team %s", tid)
  1620  		return fbo
  1621  	}
  1622  	return nil
  1623  }
  1624  
  1625  // TeamNameChanged implements the KBFSOps interface for KBFSOpsStandard
  1626  func (fs *KBFSOpsStandard) TeamNameChanged(
  1627  	ctx context.Context, tid keybase1.TeamID) {
  1628  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1629  	defer timeTrackerDone()
  1630  
  1631  	fs.log.CDebugf(ctx, "Got TeamNameChanged for %s", tid)
  1632  
  1633  	// Invalidate any cached ID->name mapping for the team if its name
  1634  	// changed, since team names can change due to SBS resolutions
  1635  	// (for implicit teams) or for subteam renames.
  1636  	fs.config.KBPKI().InvalidateTeamCacheForID(tid)
  1637  
  1638  	fbo := fs.findTeamByID(ctx, tid)
  1639  	if fbo != nil {
  1640  		go fbo.TeamNameChanged(ctx, tid)
  1641  	}
  1642  }
  1643  
  1644  // TeamAbandoned implements the KBFSOps interface for KBFSOpsStandard.
  1645  func (fs *KBFSOpsStandard) TeamAbandoned(
  1646  	ctx context.Context, tid keybase1.TeamID) {
  1647  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1648  	defer timeTrackerDone()
  1649  
  1650  	fs.log.CDebugf(ctx, "Got TeamAbandoned for %s", tid)
  1651  	fbo := fs.findTeamByID(ctx, tid)
  1652  	if fbo != nil {
  1653  		go fbo.TeamAbandoned(ctx, tid)
  1654  	}
  1655  }
  1656  
  1657  // CheckMigrationPerms implements the KBFSOps interface for folderBranchOps.
  1658  func (fs *KBFSOpsStandard) CheckMigrationPerms(
  1659  	ctx context.Context, id tlf.ID) (err error) {
  1660  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1661  	defer timeTrackerDone()
  1662  
  1663  	// We currently only migrate on the master branch of a TLF.
  1664  	ops := fs.getOps(
  1665  		ctx, data.FolderBranch{Tlf: id, Branch: data.MasterBranch},
  1666  		FavoritesOpNoChange)
  1667  	return ops.CheckMigrationPerms(ctx, id)
  1668  }
  1669  
  1670  // MigrateToImplicitTeam implements the KBFSOps interface for KBFSOpsStandard.
  1671  func (fs *KBFSOpsStandard) MigrateToImplicitTeam(
  1672  	ctx context.Context, id tlf.ID) error {
  1673  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1674  	defer timeTrackerDone()
  1675  
  1676  	// We currently only migrate on the master branch of a TLF.
  1677  	ops := fs.getOps(ctx,
  1678  		data.FolderBranch{Tlf: id, Branch: data.MasterBranch}, FavoritesOpNoChange)
  1679  	return ops.MigrateToImplicitTeam(ctx, id)
  1680  }
  1681  
  1682  // KickoffAllOutstandingRekeys implements the KBFSOps interface for
  1683  // KBFSOpsStandard.
  1684  func (fs *KBFSOpsStandard) KickoffAllOutstandingRekeys() error {
  1685  	for _, op := range fs.ops {
  1686  		op.rekeyFSM.Event(newRekeyKickoffEvent())
  1687  	}
  1688  	return nil
  1689  }
  1690  
  1691  func (fs *KBFSOpsStandard) initTLFWithoutIdentifyPopups(
  1692  	ctx context.Context, handle *tlfhandle.Handle) error {
  1693  	ctx, err := tlfhandle.MakeExtendedIdentify(
  1694  		ctx, keybase1.TLFIdentifyBehavior_KBFS_CHAT)
  1695  	if err != nil {
  1696  		return err
  1697  	}
  1698  
  1699  	_, _, err = fs.getMaybeCreateRootNode(ctx, handle, data.MasterBranch, false)
  1700  	if err != nil {
  1701  		return err
  1702  	}
  1703  
  1704  	// The popups and errors were suppressed, but any errors would
  1705  	// have been logged.  So just close out the extended identify.  If
  1706  	// the user accesses the TLF directly, another proper identify
  1707  	// should happen that shows errors.
  1708  	_ = tlfhandle.GetExtendedIdentify(ctx).GetTlfBreakAndClose()
  1709  	return nil
  1710  }
  1711  
  1712  func (fs *KBFSOpsStandard) startOpsForHistory(
  1713  	ctx context.Context, handle *tlfhandle.Handle) error {
  1714  	if fs.config.Mode().DefaultBlockRequestAction() == BlockRequestSolo {
  1715  		fb := data.FolderBranch{
  1716  			Tlf:    handle.TlfID(),
  1717  			Branch: data.MasterBranch,
  1718  		}
  1719  		ops := fs.getOpsByHandle(ctx, handle, fb, FavoritesOpNoChange)
  1720  		// Don't initialize the entire TLF, because we don't want
  1721  		// to run identifies on it.  Instead, just start the
  1722  		// chat-monitoring part.
  1723  		ops.startMonitorChat(handle.GetCanonicalName())
  1724  	} else {
  1725  		// Fully initialize the TLF in order to kick off any
  1726  		// necessary prefetches.
  1727  		err := fs.initTLFWithoutIdentifyPopups(ctx, handle)
  1728  		if err != nil {
  1729  			return err
  1730  		}
  1731  	}
  1732  	return nil
  1733  }
  1734  
  1735  // NewNotificationChannel implements the KBFSOps interface for
  1736  // KBFSOpsStandard.
  1737  func (fs *KBFSOpsStandard) NewNotificationChannel(
  1738  	ctx context.Context, handle *tlfhandle.Handle, convID chat1.ConversationID,
  1739  	channelName string) {
  1740  	if !fs.config.Mode().TLFEditHistoryEnabled() {
  1741  		return
  1742  	}
  1743  
  1744  	fs.log.CDebugf(ctx, "New notification channel for %s",
  1745  		handle.GetCanonicalPath())
  1746  
  1747  	// If the FBO already exists, notify it.  If the FBO doesn't exist
  1748  	// yet, we need to create it, so that it shows up in the edit
  1749  	// history.
  1750  	fs.opsLock.Lock()
  1751  	defer fs.opsLock.Unlock()
  1752  	fav := handle.ToFavorite()
  1753  	if ops, ok := fs.opsByFav[fav]; ok { // nolint
  1754  		ops.NewNotificationChannel(ctx, handle, convID, channelName)
  1755  	} else if handle.TlfID() != tlf.NullID {
  1756  		fs.editActivity.Add(1)
  1757  		go func() {
  1758  			defer fs.editActivity.Done()
  1759  			fs.log.CDebugf(ctx, "Initializing TLF %s for the edit history",
  1760  				handle.GetCanonicalPath())
  1761  			ctx := CtxWithRandomIDReplayable(
  1762  				context.Background(), CtxFBOIDKey, CtxFBOOpID, fs.log)
  1763  			err := fs.startOpsForHistory(ctx, handle)
  1764  			if err != nil {
  1765  				fs.log.CDebugf(ctx, "Couldn't initialize TLF: %+v", err)
  1766  			}
  1767  		}()
  1768  	} else {
  1769  		fs.log.CWarningf(ctx,
  1770  			"Handle %s for existing folder unexpectedly has no TLF ID",
  1771  			handle.GetCanonicalName())
  1772  	}
  1773  	fs.favs.RefreshCacheWhenMTimeChanged(ctx, handle.TlfID())
  1774  }
  1775  
  1776  // Reset implements the KBFSOps interface for KBFSOpsStandard.
  1777  func (fs *KBFSOpsStandard) Reset(
  1778  	ctx context.Context, handle *tlfhandle.Handle, newTlfID *tlf.ID) error {
  1779  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1780  	defer timeTrackerDone()
  1781  
  1782  	// First, make sure the folder has been reset according to the
  1783  	// mdserver.
  1784  	bareHandle, err := handle.ToBareHandle()
  1785  	if err != nil {
  1786  		return err
  1787  	}
  1788  
  1789  	if newTlfID != nil {
  1790  		oldPath := handle.GetCanonicalPath()
  1791  		fs.log.CDebugf(
  1792  			ctx, "Checking that %s is an appropriate ID for TLF %s after reset",
  1793  			*newTlfID, oldPath)
  1794  		md, err := fs.config.MDOps().GetForTLF(ctx, *newTlfID, nil)
  1795  		if err != nil {
  1796  			return err
  1797  		}
  1798  		newPath := md.GetTlfHandle().GetCanonicalPath()
  1799  		if newPath != oldPath {
  1800  			return errors.Errorf("Cannot reset %s (%s) to TLF %s (%s)",
  1801  				oldPath, handle.TlfID(), newPath, *newTlfID)
  1802  		}
  1803  	} else {
  1804  		id, _, err := fs.config.MDServer().GetForHandle(
  1805  			ctx, bareHandle, kbfsmd.Merged, nil)
  1806  		if err == nil {
  1807  			fs.log.CDebugf(ctx, "Folder %s can't be reset; still has ID %s",
  1808  				handle.GetCanonicalPath(), id)
  1809  			return errors.WithStack(FolderNotResetOnServer{handle})
  1810  		} else if _, ok := errors.Cause(err).(kbfsmd.ServerErrorClassicTLFDoesNotExist); !ok {
  1811  			// Return errors if they don't indicate the folder is new.
  1812  			return err
  1813  		}
  1814  	}
  1815  
  1816  	fs.opsLock.Lock()
  1817  	defer fs.opsLock.Unlock()
  1818  	fs.log.CDebugf(ctx, "Reset %s", handle.GetCanonicalPath())
  1819  	fb := data.FolderBranch{Tlf: handle.TlfID(), Branch: data.MasterBranch}
  1820  	ops, ok := fs.ops[fb]
  1821  	if ok {
  1822  		fs.config.MDServer().CancelRegistration(ctx, handle.TlfID())
  1823  
  1824  		err := ops.Reset(ctx, handle)
  1825  		if err != nil {
  1826  			return err
  1827  		}
  1828  		delete(fs.ops, fb)
  1829  		fav := handle.ToFavorite()
  1830  		delete(fs.opsByFav, fav)
  1831  		err = ops.Shutdown(ctx)
  1832  		if err != nil {
  1833  			return err
  1834  		}
  1835  	}
  1836  
  1837  	// Reset the TLF by overwriting the TLF ID in the sigchain.  This
  1838  	// assumes that the server is in implicit team mode for new TLFs,
  1839  	// which at this point it should always be.
  1840  	return fs.resetTlfID(ctx, handle, newTlfID)
  1841  }
  1842  
  1843  // ClearConflictView resets a TLF's journal and conflict DB to a non
  1844  // -conflicting state.
  1845  func (fs *KBFSOpsStandard) ClearConflictView(ctx context.Context,
  1846  	tlfID tlf.ID) error {
  1847  	fbo := fs.getOpsNoAdd(ctx, data.FolderBranch{
  1848  		Tlf:    tlfID,
  1849  		Branch: data.MasterBranch,
  1850  	})
  1851  	return fbo.clearConflictView(ctx)
  1852  }
  1853  
  1854  func (fs *KBFSOpsStandard) deleteOps(
  1855  	ctx context.Context, ops *folderBranchOps, fb data.FolderBranch) error {
  1856  	handle, err := ops.GetTLFHandle(ctx, nil)
  1857  	if err != nil {
  1858  		return err
  1859  	}
  1860  	fs.opsLock.Lock()
  1861  	defer fs.opsLock.Unlock()
  1862  	delete(fs.ops, fb)
  1863  	fav := handle.ToFavorite()
  1864  	delete(fs.opsByFav, fav)
  1865  	return nil
  1866  }
  1867  
  1868  // FinishResolvingConflict implements the KBFSOps interface for
  1869  // KBFSOpsStandard.
  1870  func (fs *KBFSOpsStandard) FinishResolvingConflict(
  1871  	ctx context.Context, fb data.FolderBranch) (err error) {
  1872  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1873  	defer timeTrackerDone()
  1874  
  1875  	fs.log.CDebugf(ctx, "FinishResolvingConflict(%v)", fb)
  1876  	defer func() {
  1877  		fs.deferLog.CDebugf(ctx, "Done: %+v", err)
  1878  	}()
  1879  
  1880  	// First invalidate all its nodes and shut down the FBO.
  1881  	ops := fs.getOpsIfExists(ctx, fb)
  1882  	if ops != nil {
  1883  		err := ops.invalidateAllNodes(ctx)
  1884  		if err != nil {
  1885  			return err
  1886  		}
  1887  		err = fs.deleteOps(ctx, ops, fb)
  1888  		if err != nil {
  1889  			return err
  1890  		}
  1891  		err = ops.Shutdown(ctx)
  1892  		if err != nil {
  1893  			return err
  1894  		}
  1895  	}
  1896  
  1897  	jManager, jErr := GetJournalManager(fs.config)
  1898  	if jErr == nil {
  1899  		err := jManager.FinishResolvingConflict(ctx, fb.Tlf)
  1900  		if err != nil {
  1901  			return err
  1902  		}
  1903  	}
  1904  	return nil
  1905  }
  1906  
  1907  // ForceStuckConflictForTesting implements the KBFSOps interface for
  1908  // KBFSOpsStandard.
  1909  func (fs *KBFSOpsStandard) ForceStuckConflictForTesting(
  1910  	ctx context.Context, tlfID tlf.ID) error {
  1911  	fbo := fs.getOpsNoAdd(ctx, data.FolderBranch{
  1912  		Tlf:    tlfID,
  1913  		Branch: data.MasterBranch,
  1914  	})
  1915  	// Make sure the FBO is initialized.
  1916  	_, _, _, err := fbo.getRootNode(ctx)
  1917  	if err != nil {
  1918  		return err
  1919  	}
  1920  	return fbo.forceStuckConflictForTesting(ctx)
  1921  }
  1922  
  1923  // CancelUploads implements the KBFSOps interface for KBFSOpsStandard.
  1924  func (fs *KBFSOpsStandard) CancelUploads(
  1925  	ctx context.Context, folderBranch data.FolderBranch) error {
  1926  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1927  	defer timeTrackerDone()
  1928  
  1929  	ops := fs.getOps(ctx, folderBranch, FavoritesOpAdd)
  1930  	return ops.CancelUploads(ctx, folderBranch)
  1931  }
  1932  
  1933  // GetSyncConfig implements the KBFSOps interface for KBFSOpsStandard.
  1934  func (fs *KBFSOpsStandard) GetSyncConfig(
  1935  	ctx context.Context, tlfID tlf.ID) (keybase1.FolderSyncConfig, error) {
  1936  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1937  	defer timeTrackerDone()
  1938  
  1939  	ops := fs.getOps(ctx,
  1940  		data.FolderBranch{Tlf: tlfID, Branch: data.MasterBranch}, FavoritesOpNoChange)
  1941  	return ops.GetSyncConfig(ctx, tlfID)
  1942  }
  1943  
  1944  // SetSyncConfig implements the KBFSOps interface for KBFSOpsStandard.
  1945  func (fs *KBFSOpsStandard) SetSyncConfig(
  1946  	ctx context.Context, tlfID tlf.ID,
  1947  	config keybase1.FolderSyncConfig) (<-chan error, error) {
  1948  	timeTrackerDone := fs.longOperationDebugDumper.Begin(ctx)
  1949  	defer timeTrackerDone()
  1950  
  1951  	ops := fs.getOps(ctx,
  1952  		data.FolderBranch{Tlf: tlfID, Branch: data.MasterBranch}, FavoritesOpNoChange)
  1953  	return ops.SetSyncConfig(ctx, tlfID, config)
  1954  }
  1955  
  1956  // GetAllSyncedTlfMDs implements the KBFSOps interface for KBFSOpsStandard.
  1957  func (fs *KBFSOpsStandard) GetAllSyncedTlfMDs(
  1958  	ctx context.Context) map[tlf.ID]SyncedTlfMD {
  1959  	tlfIDs := fs.config.GetAllSyncedTlfs()
  1960  	if len(tlfIDs) == 0 {
  1961  		return nil
  1962  	}
  1963  
  1964  	res := make(map[tlf.ID]SyncedTlfMD, len(tlfIDs))
  1965  
  1966  	// Add the sync config mode to each favorite.
  1967  	for _, id := range tlfIDs {
  1968  		fb := data.FolderBranch{Tlf: id, Branch: data.MasterBranch}
  1969  		md, h, err := fs.GetRootNodeMetadata(ctx, fb)
  1970  		if err != nil {
  1971  			fs.log.CDebugf(
  1972  				ctx, "Couldn't get sync config for TLF %s: %+v", id, err)
  1973  			continue
  1974  		}
  1975  
  1976  		res[id] = SyncedTlfMD{MD: md, Handle: h}
  1977  	}
  1978  	return res
  1979  }
  1980  
  1981  func (fs *KBFSOpsStandard) changeHandle(ctx context.Context,
  1982  	oldFav favorites.Folder, newHandle *tlfhandle.Handle) {
  1983  	fs.opsLock.Lock()
  1984  	defer fs.opsLock.Unlock()
  1985  	ops, ok := fs.opsByFav[oldFav]
  1986  	if !ok {
  1987  		return
  1988  	}
  1989  	newFav := newHandle.ToFavorite()
  1990  	fs.log.CDebugf(ctx, "Changing handle: %v -> %v", oldFav, newFav)
  1991  	fs.opsByFav[newFav] = ops
  1992  	delete(fs.opsByFav, oldFav)
  1993  }
  1994  
  1995  // AddRootNodeWrapper implements the KBFSOps interface for
  1996  // KBFSOpsStandard.
  1997  func (fs *KBFSOpsStandard) AddRootNodeWrapper(f func(Node) Node) {
  1998  	fs.opsLock.Lock()
  1999  	defer fs.opsLock.Unlock()
  2000  	for _, op := range fs.ops {
  2001  		op.addRootNodeWrapper(f)
  2002  	}
  2003  }
  2004  
  2005  // StatusOfServices implements the KBFSOps interface for
  2006  // KBFSOpsStandard.
  2007  func (fs *KBFSOpsStandard) StatusOfServices() (map[string]error, chan StatusUpdate) {
  2008  	return fs.currentStatus.CurrentStatus()
  2009  }
  2010  
  2011  // Notifier:
  2012  var _ Notifier = (*KBFSOpsStandard)(nil)
  2013  
  2014  // RegisterForChanges implements the Notifer interface for KBFSOpsStandard
  2015  func (fs *KBFSOpsStandard) RegisterForChanges(
  2016  	folderBranches []data.FolderBranch, obs Observer) error {
  2017  	for _, fb := range folderBranches {
  2018  		// TODO: add branch parameter to notifier interface
  2019  		ops := fs.getOps(context.Background(), fb, FavoritesOpNoChange)
  2020  		return ops.RegisterForChanges(obs)
  2021  	}
  2022  	return nil
  2023  }
  2024  
  2025  // UnregisterFromChanges implements the Notifer interface for KBFSOpsStandard
  2026  func (fs *KBFSOpsStandard) UnregisterFromChanges(
  2027  	folderBranches []data.FolderBranch, obs Observer) error {
  2028  	for _, fb := range folderBranches {
  2029  		// TODO: add branch parameter to notifier interface
  2030  		ops := fs.getOps(context.Background(), fb, FavoritesOpNoChange)
  2031  		return ops.UnregisterFromChanges(obs)
  2032  	}
  2033  	return nil
  2034  }
  2035  
  2036  // RegisterForSyncedTlfs implements the Notifer interface for KBFSOpsStandard
  2037  func (fs *KBFSOpsStandard) RegisterForSyncedTlfs(obs SyncedTlfObserver) error {
  2038  	fs.syncedTlfObservers.add(obs)
  2039  	return nil
  2040  }
  2041  
  2042  // UnregisterFromSyncedTlfs implements the Notifer interface for KBFSOpsStandard
  2043  func (fs *KBFSOpsStandard) UnregisterFromSyncedTlfs(
  2044  	obs SyncedTlfObserver) error {
  2045  	fs.syncedTlfObservers.remove(obs)
  2046  	return nil
  2047  }
  2048  
  2049  func (fs *KBFSOpsStandard) onTLFBranchChange(tlfID tlf.ID, newBID kbfsmd.BranchID) {
  2050  	ops := fs.getOps(context.Background(),
  2051  		data.FolderBranch{Tlf: tlfID, Branch: data.MasterBranch}, FavoritesOpNoChange)
  2052  	ops.onTLFBranchChange(newBID) // folderBranchOps makes a goroutine
  2053  }
  2054  
  2055  func (fs *KBFSOpsStandard) onMDFlush(tlfID tlf.ID, bid kbfsmd.BranchID,
  2056  	rev kbfsmd.Revision) {
  2057  	ops := fs.getOps(context.Background(),
  2058  		data.FolderBranch{Tlf: tlfID, Branch: data.MasterBranch}, FavoritesOpNoChange)
  2059  	ops.onMDFlush(bid, rev) // folderBranchOps makes a goroutine
  2060  }
  2061  
  2062  func (fs *KBFSOpsStandard) startInitEdit() (
  2063  	ctx context.Context, cancel context.CancelFunc,
  2064  	reqChan <-chan struct{}, doneChan chan<- struct{}) {
  2065  	fs.initLock.Lock()
  2066  	defer fs.initLock.Unlock()
  2067  	ctx = CtxWithRandomIDReplayable(
  2068  		context.Background(), CtxFBOIDKey, CtxFBOOpID, fs.log)
  2069  	ctx, cancel = context.WithCancel(ctx)
  2070  	ctx = context.WithValue(ctx, ctxKBFSOpsSkipEditHistoryBlock, struct{}{})
  2071  	if fs.initEditCancel != nil {
  2072  		fs.initEditCancel()
  2073  	}
  2074  	fs.initEditCancel = cancel
  2075  	fs.initEditReq = make(chan struct{}, 1)
  2076  	fs.initEditDone = make(chan struct{})
  2077  	return ctx, cancel, fs.initEditReq, fs.initEditDone
  2078  }
  2079  
  2080  func (fs *KBFSOpsStandard) initTlfsForEditHistories() {
  2081  	defer fs.editActivity.Done()
  2082  	shutdown := func() bool {
  2083  		fs.editLock.Lock()
  2084  		defer fs.editLock.Unlock()
  2085  		return fs.editShutdown
  2086  	}()
  2087  	if shutdown {
  2088  		return
  2089  	}
  2090  
  2091  	if !fs.config.Mode().TLFEditHistoryEnabled() {
  2092  		return
  2093  	}
  2094  
  2095  	ctx, cancel, reqChan, doneChan := fs.startInitEdit()
  2096  	defer cancel()
  2097  	defer close(doneChan)
  2098  
  2099  	select {
  2100  	case <-fs.initDoneCh:
  2101  	case <-ctx.Done():
  2102  		return
  2103  	}
  2104  
  2105  	doBlock := fs.config.Mode().BlockTLFEditHistoryIntialization()
  2106  	if doBlock {
  2107  		fs.log.CDebugf(ctx, "Waiting for a TLF edit history request")
  2108  		select {
  2109  		case <-reqChan:
  2110  		case <-ctx.Done():
  2111  			return
  2112  		}
  2113  	} else {
  2114  		time.Sleep(fs.config.Mode().InitialDelayForBackgroundWork())
  2115  		select {
  2116  		case <-ctx.Done():
  2117  			return
  2118  		default:
  2119  		}
  2120  	}
  2121  
  2122  	fs.log.CDebugf(ctx, "Querying the kbfs-edits inbox for new TLFs")
  2123  	handles, err := fs.config.Chat().GetGroupedInbox(
  2124  		ctx, chat1.TopicType_KBFSFILEEDIT, kbfsedits.MaxClusters)
  2125  	if err != nil {
  2126  		fs.log.CWarningf(ctx, "Can't get inbox: %+v", err)
  2127  		return
  2128  	}
  2129  
  2130  	// Construct folderBranchOps instances for each TLF in the inbox
  2131  	// that doesn't have one yet.
  2132  	for _, h := range handles {
  2133  		select {
  2134  		case <-ctx.Done():
  2135  			return
  2136  		default:
  2137  		}
  2138  
  2139  		if h.TlfID() != tlf.NullID {
  2140  			fs.log.CDebugf(ctx, "Initializing TLF %s (%s) for the edit history",
  2141  				h.GetCanonicalPath(), h.TlfID())
  2142  			err := fs.startOpsForHistory(ctx, h)
  2143  			if err != nil {
  2144  				fs.log.CDebugf(ctx, "Couldn't initialize TLF: %+v", err)
  2145  				continue
  2146  			}
  2147  		} else {
  2148  			fs.log.CWarningf(ctx,
  2149  				"Handle %s for existing folder unexpectedly has no TLF ID",
  2150  				h.GetCanonicalName())
  2151  		}
  2152  		if !doBlock {
  2153  			time.Sleep(fs.config.Mode().BackgroundWorkPeriod())
  2154  		}
  2155  	}
  2156  }
  2157  
  2158  func (fs *KBFSOpsStandard) startInitSync() (
  2159  	context.Context, context.CancelFunc) {
  2160  	fs.initLock.Lock()
  2161  	defer fs.initLock.Unlock()
  2162  	ctx := CtxWithRandomIDReplayable(
  2163  		context.Background(), CtxFBOIDKey, CtxFBOOpID, fs.log)
  2164  	ctx, cancel := context.WithCancel(ctx)
  2165  	if fs.initSyncCancel != nil {
  2166  		fs.initSyncCancel()
  2167  	}
  2168  	fs.initSyncCancel = cancel
  2169  	return ctx, cancel
  2170  }
  2171  
  2172  func (fs *KBFSOpsStandard) initSyncedTlfs() {
  2173  	if fs.config.MDServer() == nil {
  2174  		return
  2175  	}
  2176  
  2177  	tlfs := fs.config.GetAllSyncedTlfs()
  2178  	if len(tlfs) == 0 {
  2179  		return
  2180  	}
  2181  
  2182  	ctx, cancel := fs.startInitSync()
  2183  	defer cancel()
  2184  
  2185  	select {
  2186  	case <-fs.initDoneCh:
  2187  	case <-ctx.Done():
  2188  		return
  2189  	}
  2190  
  2191  	time.Sleep(fs.config.Mode().InitialDelayForBackgroundWork())
  2192  
  2193  	select {
  2194  	case <-ctx.Done():
  2195  		return
  2196  	default:
  2197  	}
  2198  
  2199  	fs.log.CDebugf(ctx, "Initializing %d synced TLFs", len(tlfs))
  2200  
  2201  	// Should we parallelize these in some limited way to speed it up
  2202  	// without overwhelming the CPU?
  2203  	for _, tlfID := range tlfs {
  2204  		select {
  2205  		case <-ctx.Done():
  2206  			return
  2207  		default:
  2208  		}
  2209  
  2210  		fs.log.CDebugf(ctx, "Initializing synced TLF: %s", tlfID)
  2211  		md, err := fs.config.MDOps().GetForTLF(ctx, tlfID, nil)
  2212  		if err != nil {
  2213  			// Could be that the logged-in user doesn't have access to
  2214  			// read this particular synced TLF.
  2215  			fs.log.CDebugf(ctx, "Couldn't initialize TLF %s: %+v", tlfID, err)
  2216  			continue
  2217  		}
  2218  		if md == (ImmutableRootMetadata{}) {
  2219  			fs.log.CDebugf(ctx, "TLF %s has no revisions yet", err)
  2220  			continue
  2221  		}
  2222  
  2223  		// Getting the root node populates the head of the TLF, which
  2224  		// kicks off any needed sync operations.
  2225  		err = fs.initTLFWithoutIdentifyPopups(ctx, md.GetTlfHandle())
  2226  		if err != nil {
  2227  			fs.log.CDebugf(ctx, "Couldn't initialize TLF %s: %+v", err)
  2228  			continue
  2229  		}
  2230  		time.Sleep(fs.config.Mode().BackgroundWorkPeriod())
  2231  	}
  2232  }
  2233  
  2234  // kbfsOpsFavoriteObserver deals with a handle change for a particular
  2235  // favorites.  It ignores local and batch changes.
  2236  type kbfsOpsFavoriteObserver struct {
  2237  	kbfsOps *KBFSOpsStandard
  2238  
  2239  	lock    sync.Mutex
  2240  	currFav favorites.Folder
  2241  }
  2242  
  2243  var _ Observer = (*kbfsOpsFavoriteObserver)(nil)
  2244  
  2245  func (kofo *kbfsOpsFavoriteObserver) LocalChange(
  2246  	_ context.Context, _ Node, _ WriteRange) {
  2247  }
  2248  
  2249  func (kofo *kbfsOpsFavoriteObserver) BatchChanges(
  2250  	_ context.Context, _ []NodeChange, _ []NodeID) {
  2251  }
  2252  
  2253  func (kofo *kbfsOpsFavoriteObserver) TlfHandleChange(
  2254  	ctx context.Context, newHandle *tlfhandle.Handle) {
  2255  	kofo.lock.Lock()
  2256  	defer kofo.lock.Unlock()
  2257  	kofo.kbfsOps.changeHandle(ctx, kofo.currFav, newHandle)
  2258  	kofo.currFav = newHandle.ToFavorite()
  2259  }