github.com/keybase/client/go@v0.0.0-20240520164431-4f512a4c85a3/kbfs/simplefs/simplefs.go (about)

     1  // Copyright 2016-2017 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 simplefs
     6  
     7  import (
     8  	"encoding/base64"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"mime"
    14  	"net/http"
    15  	"net/url"
    16  	"os"
    17  	"path"
    18  	stdpath "path"
    19  	"path/filepath"
    20  	"regexp"
    21  	"sort"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/keybase/client/go/kbfs/data"
    27  	"github.com/keybase/client/go/kbfs/env"
    28  	"github.com/keybase/client/go/kbfs/idutil"
    29  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    30  	"github.com/keybase/client/go/kbfs/kbfsgit"
    31  	"github.com/keybase/client/go/kbfs/kbfsmd"
    32  	"github.com/keybase/client/go/kbfs/libcontext"
    33  	"github.com/keybase/client/go/kbfs/libfs"
    34  	"github.com/keybase/client/go/kbfs/libgit"
    35  	"github.com/keybase/client/go/kbfs/libhttpserver"
    36  	"github.com/keybase/client/go/kbfs/libkbfs"
    37  	"github.com/keybase/client/go/kbfs/search"
    38  	"github.com/keybase/client/go/kbfs/tlf"
    39  	"github.com/keybase/client/go/kbfs/tlfhandle"
    40  	"github.com/keybase/client/go/libkb"
    41  	"github.com/keybase/client/go/logger"
    42  	"github.com/keybase/client/go/protocol/keybase1"
    43  	"github.com/keybase/client/go/runtimestats"
    44  	"github.com/pkg/errors"
    45  	"golang.org/x/net/context"
    46  	"golang.org/x/sync/errgroup"
    47  	billy "gopkg.in/src-d/go-billy.v4"
    48  	"gopkg.in/src-d/go-billy.v4/osfs"
    49  )
    50  
    51  const (
    52  	// CtxOpID is the display name for the unique operation SimpleFS ID tag.
    53  	ctxOpID = "SFSID"
    54  )
    55  
    56  // CtxTagKey is the type used for unique context tags
    57  type ctxTagKey int
    58  
    59  const (
    60  	// CtxIDKey is the type of the tag for unique operation IDs.
    61  	ctxIDKey ctxTagKey = iota
    62  )
    63  
    64  // simpleFSError wraps errors for SimpleFS
    65  type simpleFSError struct {
    66  	reason string
    67  	code   keybase1.StatusCode
    68  }
    69  
    70  // Error implements the error interface for simpleFSError
    71  func (e simpleFSError) Error() string { return e.reason }
    72  
    73  // ToStatus implements the keybase1.ToStatusAble interface for simpleFSError
    74  func (e simpleFSError) ToStatus() keybase1.Status {
    75  	code := e.code
    76  	if code == 0 {
    77  		code = keybase1.StatusCode_SCGeneric
    78  	}
    79  	return keybase1.Status{
    80  		Name: e.reason,
    81  		Code: int(code),
    82  		Desc: e.Error(),
    83  	}
    84  }
    85  
    86  var errOnlyRemotePathSupported = simpleFSError{reason: "Only remote paths are supported for this operation"}
    87  var errInvalidRemotePath = simpleFSError{reason: "Invalid remote path"}
    88  var errNoSuchHandle = simpleFSError{reason: "No such handle"}
    89  var errNoResult = simpleFSError{reason: "Async result not found"}
    90  var errNameExists = simpleFSError{code: keybase1.StatusCode_SCSimpleFSNameExists, reason: "name exists"}
    91  var errDirNotEmpty = simpleFSError{code: keybase1.StatusCode_SCSimpleFSDirNotEmpty, reason: "dir not empty"}
    92  var errNotExist = simpleFSError{code: keybase1.StatusCode_SCSimpleFSNotExist, reason: "file or folder does not exist"}
    93  var errNoAccess = simpleFSError{code: keybase1.StatusCode_SCSimpleFSNoAccess, reason: "no access to this file or folder"}
    94  
    95  func translateErr(err error) error {
    96  	cause := errors.Cause(err)
    97  
    98  	switch {
    99  	case os.IsExist(cause):
   100  		return errNameExists
   101  	case os.IsNotExist(cause):
   102  		return errNotExist
   103  	case cause == os.ErrPermission:
   104  		return errNoAccess
   105  	}
   106  
   107  	switch cause.(type) {
   108  	case data.NameExistsError:
   109  		return errNameExists
   110  	case libkbfs.DirNotEmptyError:
   111  		return errDirNotEmpty
   112  	case idutil.BadTLFNameError, tlf.BadNameError, tlfhandle.ReadAccessError:
   113  		return errNoAccess
   114  	}
   115  
   116  	return err
   117  }
   118  
   119  type newFSFunc func(
   120  	context.Context, libkbfs.Config, *tlfhandle.Handle, data.BranchName,
   121  	string, bool) (billy.Filesystem, error)
   122  
   123  func defaultNewFS(ctx context.Context, config libkbfs.Config,
   124  	tlfHandle *tlfhandle.Handle, branch data.BranchName, subdir string,
   125  	create bool) (
   126  	billy.Filesystem, error) {
   127  	maker := libfs.NewFS
   128  	if !create {
   129  		maker = libfs.NewFSIfExists
   130  	}
   131  	return maker(
   132  		ctx, config, tlfHandle, branch, subdir, "", keybase1.MDPriorityNormal)
   133  }
   134  
   135  // SimpleFS is the simple filesystem rpc layer implementation.
   136  type SimpleFS struct {
   137  	// logs for logging - constant, do not need locking.
   138  	log  logger.Logger
   139  	vlog *libkb.VDebugLog
   140  	// config for the fs - constant, does not need locking.
   141  	config libkbfs.Config
   142  	// The function to call for constructing a new KBFS file system.
   143  	// Overrideable for testing purposes.
   144  	newFS newFSFunc
   145  	// For dumping debug info to the logs.
   146  	idd *libkbfs.ImpatientDebugDumper
   147  
   148  	// lock protects handles and inProgress
   149  	lock sync.RWMutex
   150  	// handles contains handles opened by SimpleFSOpen,
   151  	// closed by SimpleFSClose (or SimpleFSCancel) and used
   152  	// by listing, reading and writing.
   153  	handles map[keybase1.OpID]*handle
   154  	// inProgress is for keeping state of operations in progress,
   155  	// values are removed by SimpleFSWait (or SimpleFSCancel).
   156  	inProgress map[keybase1.OpID]*inprogress
   157  
   158  	subscribeLock               sync.RWMutex
   159  	subscribeCurrTlfPathFromGUI string
   160  	subscribeCurrFB             data.FolderBranch
   161  	subscribeToEmptyTlf         string
   162  
   163  	localHTTPServer *libhttpserver.Server
   164  
   165  	subscriptionNotifier libkbfs.SubscriptionNotifier
   166  
   167  	shutdownLock    sync.RWMutex
   168  	downloadManager *downloadManager
   169  	uploadManager   *uploadManager
   170  	archiveManager  *archiveManager
   171  	indexer         *search.Indexer // Indexes synced TLFs.
   172  
   173  	httpClient *http.Client
   174  }
   175  
   176  var _ libkbfs.ResetForLoginer = (*SimpleFS)(nil)
   177  
   178  func (k *SimpleFS) getDownloadManager() *downloadManager {
   179  	k.shutdownLock.RLock()
   180  	defer k.shutdownLock.RUnlock()
   181  	return k.downloadManager
   182  }
   183  
   184  func (k *SimpleFS) getUploadManager() *uploadManager {
   185  	k.shutdownLock.RLock()
   186  	defer k.shutdownLock.RUnlock()
   187  	return k.uploadManager
   188  }
   189  
   190  func (k *SimpleFS) getArchiveManager() *archiveManager {
   191  	k.shutdownLock.RLock()
   192  	defer k.shutdownLock.RUnlock()
   193  	return k.archiveManager
   194  }
   195  
   196  func (k *SimpleFS) getIndexer() *search.Indexer {
   197  	k.shutdownLock.RLock()
   198  	defer k.shutdownLock.RUnlock()
   199  	return k.indexer
   200  }
   201  
   202  // ResetForLogin (partially) resets simplefs state when user logs out.
   203  func (k *SimpleFS) ResetForLogin(ctx context.Context,
   204  	username libkb.NormalizedUsername) (err error) {
   205  	k.log.CDebugf(ctx, "+ SimpleFS.ResetForLogin username=%s", username)
   206  	defer func() { k.log.CDebugf(ctx, "- SimpleFS.ResetForLogin err=%v", err) }()
   207  
   208  	k.shutdownLock.Lock()
   209  	defer k.shutdownLock.Unlock()
   210  
   211  	k.archiveManager.shutdown(ctx)
   212  	k.archiveManager, err = newArchiveManager(k, username)
   213  	if err != nil {
   214  		log.Fatalf("initializing archive manager error: %v", err)
   215  	}
   216  
   217  	if k.indexer != nil {
   218  		err = k.indexer.Shutdown(ctx)
   219  		if err != nil {
   220  			return err
   221  		}
   222  		k.indexer, err = search.NewIndexer(k.config)
   223  		if err != nil {
   224  			k.log.Warning("Couldn't make indexer: %+v", err)
   225  		}
   226  	}
   227  
   228  	k.downloadManager = newDownloadManager(k)
   229  	k.uploadManager = newUploadManager(k)
   230  
   231  	return nil
   232  }
   233  
   234  type inprogress struct {
   235  	desc     keybase1.OpDescription
   236  	cancel   context.CancelFunc
   237  	done     chan error
   238  	progress keybase1.OpProgress
   239  }
   240  
   241  type handle struct {
   242  	file   billy.File
   243  	async  interface{}
   244  	path   keybase1.Path
   245  	cancel context.CancelFunc
   246  }
   247  
   248  // make sure the interface is implemented
   249  var _ keybase1.SimpleFSInterface = (*SimpleFS)(nil)
   250  
   251  // We need this wrapper because at the time simpleFS is initialized
   252  // KeybaseService is not initialized yet. So just save config here, and get
   253  // the KeybaseService() later.
   254  type subscriptionNotifier struct {
   255  	config libkbfs.Config
   256  }
   257  
   258  var _ libkbfs.SubscriptionNotifier = subscriptionNotifier{}
   259  
   260  // OnNonPathChange implements the libkbfs.SubscriptionNotifier interface.
   261  func (s subscriptionNotifier) OnPathChange(
   262  	clientID libkbfs.SubscriptionManagerClientID,
   263  	subscriptionIDs []libkbfs.SubscriptionID,
   264  	path string, topics []keybase1.PathSubscriptionTopic) {
   265  	ks := s.config.KeybaseService()
   266  	if ks == nil {
   267  		return
   268  	}
   269  	ks.OnPathChange(clientID, subscriptionIDs, path, topics)
   270  }
   271  
   272  // OnPathChange implements the libkbfs.SubscriptionNotifier interface.
   273  func (s subscriptionNotifier) OnNonPathChange(
   274  	clientID libkbfs.SubscriptionManagerClientID,
   275  	subscriptionIDs []libkbfs.SubscriptionID,
   276  	topic keybase1.SubscriptionTopic) {
   277  	ks := s.config.KeybaseService()
   278  	if ks == nil {
   279  		return
   280  	}
   281  	ks.OnNonPathChange(clientID, subscriptionIDs, topic)
   282  }
   283  
   284  func newSimpleFS(appStateUpdater env.AppStateUpdater, config libkbfs.Config) *SimpleFS {
   285  	log := config.MakeLogger("simplefs")
   286  	var localHTTPServer *libhttpserver.Server
   287  	var err error
   288  	if config.Mode().LocalHTTPServerEnabled() {
   289  		localHTTPServer, err = libhttpserver.New(appStateUpdater, config)
   290  		if err != nil {
   291  			log.Fatalf("initializing localHTTPServer error: %v", err)
   292  		}
   293  	}
   294  
   295  	var indexer *search.Indexer
   296  	if config.Mode().IndexingEnabled() {
   297  		newIndexer, err := search.NewIndexer(config)
   298  		if err != nil {
   299  			log.Warning("Couldn't make indexer: %+v", err)
   300  		} else {
   301  			indexer = newIndexer
   302  		}
   303  	}
   304  
   305  	k := &SimpleFS{
   306  		config: config,
   307  
   308  		handles:              map[keybase1.OpID]*handle{},
   309  		inProgress:           map[keybase1.OpID]*inprogress{},
   310  		log:                  log,
   311  		vlog:                 config.MakeVLogger(log),
   312  		newFS:                defaultNewFS,
   313  		idd:                  libkbfs.NewImpatientDebugDumperForForcedDumps(config),
   314  		indexer:              indexer,
   315  		localHTTPServer:      localHTTPServer,
   316  		subscriptionNotifier: subscriptionNotifier{config},
   317  		httpClient:           &http.Client{},
   318  	}
   319  	k.downloadManager = newDownloadManager(k)
   320  	k.uploadManager = newUploadManager(k)
   321  	k.archiveManager, err = newArchiveManager(k, config.KbEnv().GetUsername())
   322  	if err != nil {
   323  		log.Fatalf("initializing archive manager error: %v", err)
   324  	}
   325  	return k
   326  }
   327  
   328  // NewSimpleFS creates a new SimpleFS instance.
   329  func NewSimpleFS(
   330  	appStateUpdater env.AppStateUpdater, config libkbfs.Config) (
   331  	iface *SimpleFS,
   332  	shutdownFn func(context.Context) error) {
   333  	simpleFS := newSimpleFS(appStateUpdater, config)
   334  	return simpleFS, simpleFS.Shutdown
   335  }
   336  
   337  func (k *SimpleFS) makeContext(ctx context.Context) context.Context {
   338  	return libkbfs.CtxWithRandomIDReplayable(ctx, ctxIDKey, ctxOpID, k.log)
   339  }
   340  
   341  func (k *SimpleFS) makeContextWithIdentifyBehavior(ctx context.Context, identifyBehavior *keybase1.TLFIdentifyBehavior) (newCtx context.Context, err error) {
   342  	newCtx = libkbfs.CtxWithRandomIDReplayable(ctx, ctxIDKey, ctxOpID, k.log)
   343  	if identifyBehavior != nil {
   344  		newCtx, err = tlfhandle.MakeExtendedIdentify(newCtx, *identifyBehavior)
   345  		if err != nil {
   346  			return nil, err
   347  		}
   348  	}
   349  	return newCtx, nil
   350  }
   351  
   352  func getIdentifyBehaviorFromPath(path *keybase1.Path) (*keybase1.TLFIdentifyBehavior, error) {
   353  	if path == nil {
   354  		return nil, nil
   355  	}
   356  	pt, err := path.PathType()
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  	switch pt {
   361  	case keybase1.PathType_KBFS:
   362  		return path.Kbfs().IdentifyBehavior, nil
   363  	case keybase1.PathType_KBFS_ARCHIVED:
   364  		return path.KbfsArchived().IdentifyBehavior, nil
   365  	default:
   366  		return nil, nil
   367  	}
   368  }
   369  
   370  func populateIdentifyBehaviorIfNeeded(ctx context.Context, path1 *keybase1.Path, path2 *keybase1.Path) (context.Context, error) {
   371  	ib1, err := getIdentifyBehaviorFromPath(path1)
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  	ib2, err := getIdentifyBehaviorFromPath(path2)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  
   380  	if ib1 == nil && ib2 == nil {
   381  		return ctx, nil
   382  	}
   383  	if ib1 == nil && ib2 != nil {
   384  		return tlfhandle.MakeExtendedIdentify(ctx, *ib2)
   385  	}
   386  	if ib1 != nil && ib2 == nil {
   387  		return tlfhandle.MakeExtendedIdentify(ctx, *ib1)
   388  	}
   389  	if *ib1 == *ib2 {
   390  		return tlfhandle.MakeExtendedIdentify(ctx, *ib1)
   391  	}
   392  	return nil, errors.New("inconsistent IdentifyBehavior set in both paths")
   393  }
   394  
   395  func rawPathFromKbfsPath(path keybase1.Path) (string, error) {
   396  	pt, err := path.PathType()
   397  	if err != nil {
   398  		return "", err
   399  	}
   400  
   401  	switch pt {
   402  	case keybase1.PathType_KBFS:
   403  		return stdpath.Clean(path.Kbfs().Path), nil
   404  	case keybase1.PathType_KBFS_ARCHIVED:
   405  		return stdpath.Clean(path.KbfsArchived().Path), nil
   406  	default:
   407  		return "", errOnlyRemotePathSupported
   408  	}
   409  }
   410  
   411  func splitPathFromKbfsPath(path keybase1.Path) ([]string, error) {
   412  	raw, err := rawPathFromKbfsPath(path)
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  	if stdpath.IsAbs(raw) {
   417  		raw = raw[1:]
   418  	}
   419  	return strings.Split(raw, `/`), nil
   420  }
   421  
   422  // remoteTlfAndPath decodes a remote path for us.
   423  func remoteTlfAndPath(path keybase1.Path) (
   424  	t tlf.Type, tlfName, middlePath, finalElem string, err error) {
   425  	ps, err := splitPathFromKbfsPath(path)
   426  	if err != nil {
   427  		return tlf.Private, "", "", "", err
   428  	}
   429  	switch {
   430  	case len(ps) < 2:
   431  		return tlf.Private, "", "", "", errInvalidRemotePath
   432  	case ps[0] == `private`:
   433  		t = tlf.Private
   434  	case ps[0] == `public`:
   435  		t = tlf.Public
   436  	case ps[0] == `team`:
   437  		t = tlf.SingleTeam
   438  	default:
   439  		return tlf.Private, "", "", "", errInvalidRemotePath
   440  	}
   441  	if len(ps) >= 3 {
   442  		finalElem = ps[len(ps)-1]
   443  		middlePath = stdpath.Join(ps[2 : len(ps)-1]...)
   444  	}
   445  	return t, ps[1], middlePath, finalElem, nil
   446  }
   447  
   448  func (k *SimpleFS) branchNameFromPath(
   449  	ctx context.Context, tlfHandle *tlfhandle.Handle, path keybase1.Path) (
   450  	data.BranchName, error) {
   451  	pt, err := path.PathType()
   452  	if err != nil {
   453  		return "", err
   454  	}
   455  	switch pt {
   456  	case keybase1.PathType_KBFS:
   457  		if tlfHandle.IsLocalConflict() {
   458  			b, ok := data.MakeConflictBranchName(tlfHandle)
   459  			if ok {
   460  				return b, nil
   461  			}
   462  		}
   463  		return data.MasterBranch, nil
   464  	case keybase1.PathType_KBFS_ARCHIVED:
   465  		archivedParam := path.KbfsArchived().ArchivedParam
   466  		archivedType, err := archivedParam.KBFSArchivedType()
   467  		if err != nil {
   468  			return "", err
   469  		}
   470  		switch archivedType {
   471  		case keybase1.KBFSArchivedType_REVISION:
   472  			return data.MakeRevBranchName(
   473  				kbfsmd.Revision(archivedParam.Revision())), nil
   474  		case keybase1.KBFSArchivedType_TIME:
   475  			t := keybase1.FromTime(archivedParam.Time())
   476  			rev, err := libkbfs.GetMDRevisionByTime(ctx, k.config, tlfHandle, t)
   477  			if err != nil {
   478  				return "", err
   479  			}
   480  			return data.MakeRevBranchName(rev), nil
   481  		case keybase1.KBFSArchivedType_TIME_STRING:
   482  			t := archivedParam.TimeString()
   483  			rev, err := libfs.RevFromTimeString(ctx, k.config, tlfHandle, t)
   484  			if err != nil {
   485  				return "", err
   486  			}
   487  			return data.MakeRevBranchName(rev), nil
   488  		case keybase1.KBFSArchivedType_REL_TIME_STRING:
   489  			t := archivedParam.RelTimeString()
   490  			rev, err := libfs.RevFromRelativeTimeString(
   491  				ctx, k.config, tlfHandle, t)
   492  			if err != nil {
   493  				return "", err
   494  			}
   495  			return data.MakeRevBranchName(rev), nil
   496  		default:
   497  			return "", simpleFSError{reason: "Invalid archived type for branch name"}
   498  		}
   499  	default:
   500  		return "", simpleFSError{reason: "Invalid path type for branch name"}
   501  	}
   502  }
   503  
   504  func (k *SimpleFS) getKBPKI(ctx context.Context) (libkbfs.KBPKI, error) {
   505  	// Just make sure KBPKI exists before we try to use it.  It
   506  	// actually gets created after SimpleFS registers as an RPC
   507  	// service..
   508  	kbpki := k.config.KBPKI()
   509  	for kbpki == nil {
   510  		t := time.NewTimer(250 * time.Millisecond)
   511  		select {
   512  		case <-t.C:
   513  			kbpki = k.config.KBPKI()
   514  		case <-ctx.Done():
   515  			_ = t.Stop()
   516  			return nil, ctx.Err()
   517  		}
   518  	}
   519  	return kbpki, nil
   520  }
   521  
   522  func (k *SimpleFS) getFSWithMaybeCreate(
   523  	ctx context.Context, path keybase1.Path, create bool) (
   524  	fs billy.Filesystem, finalElem string, err error) {
   525  	pt, err := path.PathType()
   526  	if err != nil {
   527  		return nil, "", err
   528  	}
   529  	switch pt {
   530  	case keybase1.PathType_KBFS, keybase1.PathType_KBFS_ARCHIVED:
   531  		// Check for the root FS first.
   532  		ps, err := splitPathFromKbfsPath(path)
   533  		if err != nil {
   534  			return nil, "", err
   535  		}
   536  		if len(ps) < 2 || len(ps) < 3 && strings.HasPrefix(ps[0], ".kbfs_") {
   537  			fs = libfs.NewRootFS(k.config)
   538  			if len(ps) == 1 {
   539  				finalElem = ps[0]
   540  			} else if len(ps) == 2 {
   541  				fs, err = fs.Chroot(ps[0])
   542  				if err != nil {
   543  					return nil, "", err
   544  				}
   545  				finalElem = ps[1]
   546  			}
   547  			return fs, finalElem, nil
   548  		}
   549  
   550  		t, tlfName, restOfPath, finalElem, err := remoteTlfAndPath(path)
   551  		if err != nil {
   552  			return nil, "", err
   553  		}
   554  		kbpki, err := k.getKBPKI(ctx)
   555  		if err != nil {
   556  			return nil, "", err
   557  		}
   558  		tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
   559  			ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
   560  		if err != nil {
   561  			return nil, "", err
   562  		}
   563  		branch, err := k.branchNameFromPath(ctx, tlfHandle, path)
   564  		if err != nil {
   565  			return nil, "", err
   566  		}
   567  		fs, err := k.newFS(
   568  			ctx, k.config, tlfHandle, branch, restOfPath, create)
   569  		if err != nil {
   570  			if exitEarly, _ := libfs.FilterTLFEarlyExitError(
   571  				ctx, err, k.log, tlfHandle.GetCanonicalName()); exitEarly {
   572  				return nil, finalElem, libfs.TlfDoesNotExist{}
   573  			}
   574  			return nil, "", err
   575  		}
   576  		if create {
   577  			err = k.checkEmptySubscription(ctx, path)
   578  			if err != nil {
   579  				return nil, "", err
   580  			}
   581  		}
   582  		return fs, finalElem, nil
   583  	case keybase1.PathType_LOCAL:
   584  		fs = osfs.New(stdpath.Dir(path.Local()))
   585  		return fs, stdpath.Base(path.Local()), nil
   586  	default:
   587  		return nil, "", simpleFSError{reason: "Invalid path type"}
   588  	}
   589  }
   590  
   591  func (k *SimpleFS) getFS(
   592  	ctx context.Context, path keybase1.Path) (
   593  	fs billy.Filesystem, finalElem string, err error) {
   594  	return k.getFSWithMaybeCreate(ctx, path, true)
   595  }
   596  
   597  func (k *SimpleFS) getFSIfExists(
   598  	ctx context.Context, path keybase1.Path) (
   599  	fs billy.Filesystem, finalElem string, err error) {
   600  	return k.getFSWithMaybeCreate(ctx, path, false)
   601  }
   602  
   603  func deTy2Ty(et data.EntryType) keybase1.DirentType {
   604  	switch et {
   605  	case data.Exec:
   606  		return keybase1.DirentType_EXEC
   607  	case data.File:
   608  		return keybase1.DirentType_FILE
   609  	case data.Dir:
   610  		return keybase1.DirentType_DIR
   611  	case data.Sym:
   612  		return keybase1.DirentType_SYM
   613  	}
   614  	panic("deTy2Ty unreachable")
   615  }
   616  
   617  func (k *SimpleFS) favoriteList(ctx context.Context, t tlf.Type) ([]keybase1.Dirent, error) {
   618  	kbpki, err := k.getKBPKI(ctx)
   619  	if err != nil {
   620  		return nil, err
   621  	}
   622  	session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true)
   623  	if err != nil {
   624  		return nil, err
   625  	}
   626  	// Return empty directory listing if we are not logged in.
   627  	if session.UID.IsNil() {
   628  		return nil, nil
   629  	}
   630  
   631  	k.config.GetPerfLog().CDebugf(ctx, "GetFavorites simplefs.favoriteList")
   632  	favs, err := k.config.KBFSOps().GetFavorites(ctx)
   633  	if err != nil {
   634  		return nil, err
   635  	}
   636  
   637  	res := make([]keybase1.Dirent, 0, len(favs))
   638  	for _, fav := range favs {
   639  		if fav.Type != t {
   640  			continue
   641  		}
   642  		pname, err := tlf.CanonicalToPreferredName(
   643  			session.Name, tlf.CanonicalName(fav.Name))
   644  		if err != nil {
   645  			k.log.Errorf("CanonicalToPreferredName: %q %v", fav.Name, err)
   646  			continue
   647  		}
   648  		res = append(res, keybase1.Dirent{})
   649  		res[len(res)-1].Name = string(pname)
   650  		res[len(res)-1].DirentType = deTy2Ty(data.Dir)
   651  
   652  		handle, err := tlfhandle.ParseHandlePreferredQuick(
   653  			ctx, kbpki, k.config, string(pname), t)
   654  		if err != nil {
   655  			k.log.Errorf("ParseTlfHandlePreferredQuick: %s %q %v", t, pname, err)
   656  			continue
   657  		}
   658  		res[len(res)-1].Writable, err = libfs.IsWriter(
   659  			ctx, kbpki, k.config, handle)
   660  		if err != nil {
   661  			k.log.Errorf("libfs.IsWriter: %q %+v", pname, err)
   662  			continue
   663  		}
   664  	}
   665  	return res, nil
   666  }
   667  
   668  func (k *SimpleFS) setStat(
   669  	de *keybase1.Dirent, fi os.FileInfo, linkFS billy.Filesystem) error {
   670  	de.Time = keybase1.ToTime(fi.ModTime())
   671  	de.Size = int(fi.Size()) // TODO: FIX protocol
   672  
   673  	t := data.File
   674  	switch {
   675  	case fi.IsDir():
   676  		t = data.Dir
   677  	case fi.Mode()&0100 != 0:
   678  		t = data.Exec
   679  	case fi.Mode()&os.ModeSymlink != 0:
   680  		t = data.Sym
   681  
   682  		link, err := linkFS.Readlink(fi.Name())
   683  		if err != nil {
   684  			return err
   685  		}
   686  		de.SymlinkTarget = link
   687  	}
   688  	de.DirentType = deTy2Ty(t)
   689  	de.Writable = (fi.Mode()&0222 != 0)
   690  
   691  	if lwg, ok := fi.Sys().(libfs.KBFSMetadataForSimpleFSGetter); ok {
   692  		md, err := lwg.KBFSMetadataForSimpleFS()
   693  		if err != nil {
   694  			return err
   695  		}
   696  		de.LastWriterUnverified = md.LastWriter
   697  		de.PrefetchStatus = md.PrefetchStatus
   698  		de.PrefetchProgress = md.PrefetchProgress.ToProtocolProgress(
   699  			k.config.Clock())
   700  	}
   701  	de.Name = fi.Name()
   702  	return nil
   703  }
   704  
   705  func (k *SimpleFS) setResult(opid keybase1.OpID, val interface{}) {
   706  	k.lock.Lock()
   707  	k.handles[opid] = &handle{async: val}
   708  	k.lock.Unlock()
   709  }
   710  
   711  func (k *SimpleFS) startOp(ctx context.Context, opid keybase1.OpID,
   712  	opType keybase1.AsyncOps, desc keybase1.OpDescription) (
   713  	_ context.Context, w *inprogress, err error) {
   714  	ctx = k.makeContext(ctx)
   715  	ctx, cancel := context.WithCancel(ctx)
   716  	w = &inprogress{
   717  		desc,
   718  		cancel,
   719  		make(chan error, 1),
   720  		keybase1.OpProgress{OpType: opType},
   721  	}
   722  	k.lock.Lock()
   723  	k.inProgress[opid] = w
   724  	k.lock.Unlock()
   725  	// ignore error, this is just for logging.
   726  	descBS, _ := json.Marshal(desc)
   727  	k.vlog.CLogf(ctx, libkb.VLog1, "start %X %s", opid, descBS)
   728  	newCtx, err := k.startOpWrapContext(ctx)
   729  	return newCtx, w, err
   730  }
   731  
   732  func (k *SimpleFS) doneOp(ctx context.Context, opid keybase1.OpID, w *inprogress, err error) {
   733  	// We aren't accessing w.progress directionly but w can still be in there
   734  	// so is still protected by the lock.
   735  	k.lock.Lock()
   736  	w.progress.EndEstimate = keybase1.ToTime(k.config.Clock().Now())
   737  	k.lock.Unlock()
   738  
   739  	w.done <- err
   740  	close(w.done)
   741  	k.log.CDebugf(ctx, "done op %X, status=%+v", opid, err)
   742  	if ctx != nil {
   743  		err := libcontext.CleanupCancellationDelayer(ctx)
   744  		if err != nil {
   745  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
   746  		}
   747  	}
   748  }
   749  
   750  func (k *SimpleFS) startAsync(
   751  	ctx context.Context, opid keybase1.OpID, opType keybase1.AsyncOps,
   752  	desc keybase1.OpDescription,
   753  	path1ForIdentifyBehavior *keybase1.Path,
   754  	path2ForIdentifyBehavior *keybase1.Path,
   755  	callback func(context.Context) error) (err error) {
   756  	ctxAsync, w, e0 := k.startOp(context.Background(), opid, opType, desc)
   757  	if e0 != nil {
   758  		return e0
   759  	}
   760  	ctxAsync, err = populateIdentifyBehaviorIfNeeded(
   761  		ctxAsync, path1ForIdentifyBehavior, path2ForIdentifyBehavior)
   762  	if err != nil {
   763  		return err
   764  	}
   765  	// Bind the old context to the new context, for debugging purposes.
   766  	k.log.CDebugf(ctx, "Launching new async operation with SFSID=%s",
   767  		ctxAsync.Value(ctxIDKey))
   768  	go func() {
   769  		var err error
   770  		// Capture the inprogress reference here rather than let doneOp
   771  		// retrieve it when called, so that doneOp always has it when it's
   772  		// called. This is needed when SimpleFSCancel is called when a
   773  		// SimpleFSWait is already in the air. Since SimpleFSCancel deletes the
   774  		// inprogress object from k.inProgress, doneOp wouldn't be able to get
   775  		// the object when it's called. To make sure SimpleFSWait returns and
   776  		// returns the correct error, we just pass in the inprogress reference
   777  		// here.
   778  		defer func() { k.doneOp(ctxAsync, opid, w, err) }()
   779  		err = callback(ctxAsync)
   780  		if err != nil {
   781  			k.log.CDebugf(ctxAsync, "Error making async callback: %+v", err)
   782  		}
   783  	}()
   784  	return nil
   785  }
   786  
   787  func (k *SimpleFS) setProgressTotals(
   788  	opid keybase1.OpID, totalBytes, totalFiles int64) {
   789  	k.lock.Lock()
   790  	defer k.lock.Unlock()
   791  	w, ok := k.inProgress[opid]
   792  	if !ok {
   793  		return
   794  	}
   795  	w.progress.BytesTotal = totalBytes
   796  	w.progress.FilesTotal = totalFiles
   797  	w.progress.Start = keybase1.ToTime(k.config.Clock().Now())
   798  }
   799  
   800  func (k *SimpleFS) updateReadProgress(
   801  	opid keybase1.OpID, readBytes, readFiles int64) {
   802  	k.lock.Lock()
   803  	defer k.lock.Unlock()
   804  	w, ok := k.inProgress[opid]
   805  	if !ok {
   806  		return
   807  	}
   808  	w.progress.BytesRead += readBytes
   809  	if w.progress.BytesRead > w.progress.BytesTotal {
   810  		// Our original total was wrong or we didn't get one.
   811  		w.progress.BytesTotal = w.progress.BytesRead
   812  	}
   813  	w.progress.FilesRead += readFiles
   814  	if w.progress.FilesRead > w.progress.FilesTotal {
   815  		// Our original total was wrong or we didn't get one.
   816  		w.progress.FilesTotal = w.progress.FilesRead
   817  	}
   818  }
   819  
   820  func (k *SimpleFS) updateWriteProgress(
   821  	opid keybase1.OpID, wroteBytes, wroteFiles int64) {
   822  	k.lock.Lock()
   823  	defer k.lock.Unlock()
   824  	w, ok := k.inProgress[opid]
   825  	if !ok {
   826  		return
   827  	}
   828  	w.progress.BytesWritten += wroteBytes
   829  	if w.progress.BytesWritten > w.progress.BytesTotal {
   830  		// Our original total was wrong or we didn't get one.
   831  		w.progress.BytesTotal = w.progress.BytesWritten
   832  	}
   833  	w.progress.FilesWritten += wroteFiles
   834  	if w.progress.FilesWritten > w.progress.FilesTotal {
   835  		// Our original total was wrong or we didn't get one.
   836  		w.progress.FilesTotal = w.progress.FilesWritten
   837  	}
   838  }
   839  
   840  var filesToIgnore = map[string]bool{
   841  	".Trashes":   true,
   842  	".fseventsd": true,
   843  	".DS_Store":  true,
   844  }
   845  var prefixesToIgnore = []string{"._"}
   846  
   847  func isFiltered(filter keybase1.ListFilter, name string) bool {
   848  	switch filter {
   849  	case keybase1.ListFilter_NO_FILTER:
   850  		return false
   851  	case keybase1.ListFilter_FILTER_ALL_HIDDEN:
   852  		return strings.HasPrefix(name, ".")
   853  	case keybase1.ListFilter_FILTER_SYSTEM_HIDDEN:
   854  		if filesToIgnore[name] {
   855  			return true
   856  		}
   857  		for _, prefix := range prefixesToIgnore {
   858  			if strings.HasPrefix(name, prefix) {
   859  				return true
   860  			}
   861  		}
   862  		return false
   863  	}
   864  	return false
   865  }
   866  
   867  func (k *SimpleFS) getFolderBranchFromPath(
   868  	ctx context.Context, path keybase1.Path) (
   869  	data.FolderBranch, string, error) {
   870  	t, tlfName, _, _, err := remoteTlfAndPath(path)
   871  	if err != nil {
   872  		return data.FolderBranch{}, "", err
   873  	}
   874  	kbpki, err := k.getKBPKI(ctx)
   875  	if err != nil {
   876  		return data.FolderBranch{}, "", err
   877  	}
   878  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
   879  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
   880  	if err != nil {
   881  		return data.FolderBranch{}, "", err
   882  	}
   883  
   884  	// Get the root node first to initialize the TLF.
   885  	node, _, err := k.config.KBFSOps().GetRootNode(
   886  		ctx, tlfHandle, data.MasterBranch)
   887  	if err != nil {
   888  		return data.FolderBranch{}, "", err
   889  	}
   890  	if node == nil {
   891  		return data.FolderBranch{}, tlfHandle.GetCanonicalPath(), nil
   892  	}
   893  	return node.GetFolderBranch(), tlfHandle.GetCanonicalPath(), nil
   894  }
   895  
   896  func (k *SimpleFS) refreshSubscriptionLocked(
   897  	ctx context.Context, path keybase1.Path, tlfPathFromGUI string) error {
   898  	// TODO: when favorites caching is ready, handle folder-list paths
   899  	// like `/keybase/private` here.
   900  
   901  	fb, _, err := k.getFolderBranchFromPath(ctx, path)
   902  	if err != nil {
   903  		return err
   904  	}
   905  	if fb == (data.FolderBranch{}) {
   906  		k.log.CDebugf(
   907  			ctx, "Ignoring subscription for empty TLF %q", path)
   908  		k.subscribeToEmptyTlf = tlfPathFromGUI
   909  		return nil
   910  	}
   911  
   912  	if k.subscribeCurrFB == fb {
   913  		k.subscribeToEmptyTlf = ""
   914  		return nil
   915  	}
   916  
   917  	if k.subscribeCurrFB != (data.FolderBranch{}) {
   918  		err = k.config.Notifier().UnregisterFromChanges(
   919  			[]data.FolderBranch{k.subscribeCurrFB}, k)
   920  		if err != nil {
   921  			return err
   922  		}
   923  	}
   924  
   925  	k.log.CDebugf(ctx, "Subscribing to %s", tlfPathFromGUI)
   926  	err = k.config.Notifier().RegisterForChanges(
   927  		[]data.FolderBranch{fb}, k)
   928  	if err != nil {
   929  		return err
   930  	}
   931  	// We are subscribing on TLF level anyway, so just use TLF path when
   932  	// notifying GUI.
   933  	k.subscribeCurrTlfPathFromGUI = tlfPathFromGUI
   934  	k.subscribeCurrFB = fb
   935  	k.subscribeToEmptyTlf = ""
   936  	return nil
   937  }
   938  
   939  func tlfNameFromPath(path keybase1.Path) (string, error) {
   940  	pType, err := path.PathType()
   941  	if err != nil {
   942  		return "", err
   943  	}
   944  	if pType != keybase1.PathType_KBFS {
   945  		return "", nil
   946  	}
   947  
   948  	tlfType, tlfNameFromGUI, _, _, err := remoteTlfAndPath(path)
   949  	if err != nil {
   950  		return "", err
   951  	}
   952  	return tlfhandle.BuildCanonicalPathForTlfType(
   953  		tlfType, tlfNameFromGUI), nil
   954  }
   955  
   956  func (k *SimpleFS) refreshSubscription(
   957  	ctx context.Context, path keybase1.Path) error {
   958  	tlfPathFromGUI, err := tlfNameFromPath(path)
   959  	if err != nil {
   960  		return err
   961  	}
   962  	if tlfPathFromGUI == "" {
   963  		k.log.CDebugf(ctx, "Ignoring subscription for path %s", path)
   964  		return nil
   965  	}
   966  
   967  	k.subscribeLock.Lock()
   968  	defer k.subscribeLock.Unlock()
   969  	return k.refreshSubscriptionLocked(ctx, path, tlfPathFromGUI)
   970  }
   971  
   972  func (k *SimpleFS) checkEmptySubscription(
   973  	ctx context.Context, path keybase1.Path) error {
   974  	k.subscribeLock.Lock()
   975  	defer k.subscribeLock.Unlock()
   976  	if k.subscribeToEmptyTlf == "" {
   977  		// Fast path.
   978  		return nil
   979  	}
   980  
   981  	tlfPathFromGUI, err := tlfNameFromPath(path)
   982  	if err != nil {
   983  		return err
   984  	}
   985  	if tlfPathFromGUI == "" {
   986  		return nil
   987  	}
   988  
   989  	if k.subscribeToEmptyTlf != tlfPathFromGUI {
   990  		return nil
   991  	}
   992  
   993  	k.log.CDebugf(
   994  		ctx, "Trying to subscribe to %s, which was previously empty",
   995  		tlfPathFromGUI)
   996  	return k.refreshSubscriptionLocked(ctx, path, tlfPathFromGUI)
   997  }
   998  
   999  // SimpleFSList - Begin list of items in directory at path
  1000  // Retrieve results with readList()
  1001  // Cannot be a single file to get flags/status,
  1002  // must be a directory.
  1003  func (k *SimpleFS) SimpleFSList(ctx context.Context, arg keybase1.SimpleFSListArg) (err error) {
  1004  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_LIST,
  1005  		keybase1.NewOpDescriptionWithList(
  1006  			keybase1.ListArgs{
  1007  				OpID: arg.OpID, Path: arg.Path, Filter: arg.Filter,
  1008  			}),
  1009  		&arg.Path, nil,
  1010  		func(ctx context.Context) (err error) {
  1011  			defer func() { err = translateErr(err) }()
  1012  			var res []keybase1.Dirent
  1013  
  1014  			rawPath, err := rawPathFromKbfsPath(arg.Path)
  1015  			if err != nil {
  1016  				return err
  1017  			}
  1018  			switch {
  1019  			case rawPath == "/":
  1020  				res = []keybase1.Dirent{
  1021  					{Name: "private", DirentType: deTy2Ty(data.Dir)},
  1022  					{Name: "public", DirentType: deTy2Ty(data.Dir)},
  1023  					{Name: "team", DirentType: deTy2Ty(data.Dir)},
  1024  				}
  1025  			case rawPath == `/public`:
  1026  				res, err = k.favoriteList(ctx, tlf.Public)
  1027  			case rawPath == `/private`:
  1028  				res, err = k.favoriteList(ctx, tlf.Private)
  1029  			case rawPath == `/team`:
  1030  				res, err = k.favoriteList(ctx, tlf.SingleTeam)
  1031  			default:
  1032  				fs, finalElem, err := k.getFSIfExists(ctx, arg.Path)
  1033  				switch errors.Cause(err).(type) {
  1034  				case nil:
  1035  				case libfs.TlfDoesNotExist:
  1036  					// TLF doesn't exist yet; just return an empty result.
  1037  					k.setResult(arg.OpID, keybase1.SimpleFSListResult{})
  1038  					return nil
  1039  				default:
  1040  					return err
  1041  				}
  1042  
  1043  				if arg.RefreshSubscription {
  1044  					// TODO: move this higher when we handle
  1045  					// subscribing to the favorites list.
  1046  					err = k.refreshSubscription(ctx, arg.Path)
  1047  					if err != nil {
  1048  						return err
  1049  					}
  1050  				}
  1051  
  1052  				// With listing, we don't know the totals ahead of time,
  1053  				// so just start with a 0 total.
  1054  				k.setProgressTotals(arg.OpID, 0, 0)
  1055  				finalElemFI, err := fs.Lstat(finalElem)
  1056  				if err != nil {
  1057  					return err
  1058  				}
  1059  				var fis []os.FileInfo
  1060  				linkFS := fs
  1061  				if finalElemFI.IsDir() {
  1062  					fis, err = fs.ReadDir(finalElem)
  1063  					if err != nil {
  1064  						return err
  1065  					}
  1066  					linkFS, err = fs.Chroot(finalElem)
  1067  					if err != nil {
  1068  						return err
  1069  					}
  1070  				} else {
  1071  					fis = append(fis, finalElemFI)
  1072  				}
  1073  				for _, fi := range fis {
  1074  					if finalElemFI.IsDir() &&
  1075  						isFiltered(arg.Filter, fi.Name()) {
  1076  						continue
  1077  					}
  1078  
  1079  					var d keybase1.Dirent
  1080  					err := k.setStat(&d, fi, linkFS)
  1081  					if err != nil {
  1082  						return err
  1083  					}
  1084  					res = append(res, d)
  1085  				}
  1086  				k.updateReadProgress(arg.OpID, 0, int64(len(fis)))
  1087  			}
  1088  			if err != nil {
  1089  				return err
  1090  			}
  1091  			k.setResult(arg.OpID, keybase1.SimpleFSListResult{Entries: res})
  1092  			return nil
  1093  		})
  1094  }
  1095  
  1096  // listRecursiveToDepthAsync returns a function that recursively lists folders,
  1097  // up to a given depth. A depth of -1 is treated as unlimited. The function
  1098  // also updates progress for the passed-in opID as it progresses, and then sets
  1099  // the result for the opID when it completes.
  1100  //
  1101  // TODO: refactor SimpleFSList to use this too (finalDepth = 0)
  1102  func (k *SimpleFS) listRecursiveToDepth(opID keybase1.OpID,
  1103  	path keybase1.Path, filter keybase1.ListFilter,
  1104  	finalDepth int, refreshSubscription bool) func(context.Context) error {
  1105  	return func(ctx context.Context) (err error) {
  1106  		defer func() { err = translateErr(err) }()
  1107  		// A stack of paths to process - ordering does not matter.
  1108  		// Here we don't walk symlinks, so no loops possible.
  1109  		type pathStackElem struct {
  1110  			path  string
  1111  			depth int
  1112  		}
  1113  		var paths []pathStackElem
  1114  
  1115  		fs, finalElem, err := k.getFSIfExists(ctx, path)
  1116  		switch errors.Cause(err).(type) {
  1117  		case nil:
  1118  		case libfs.TlfDoesNotExist:
  1119  			// TLF doesn't exist yet; just return an empty result.
  1120  			k.setResult(opID, keybase1.SimpleFSListResult{})
  1121  			return nil
  1122  		default:
  1123  			return err
  1124  		}
  1125  
  1126  		if refreshSubscription {
  1127  			err = k.refreshSubscription(ctx, path)
  1128  			if err != nil {
  1129  				return err
  1130  			}
  1131  		}
  1132  
  1133  		// With listing, we don't know the totals ahead of time,
  1134  		// so just start with a 0 total.
  1135  		k.setProgressTotals(opID, 0, 0)
  1136  		fi, err := fs.Lstat(finalElem)
  1137  		if err != nil {
  1138  			return err
  1139  		}
  1140  		var des []keybase1.Dirent
  1141  		if !fi.IsDir() {
  1142  			var d keybase1.Dirent
  1143  			err := k.setStat(&d, fi, fs)
  1144  			if err != nil {
  1145  				return err
  1146  			}
  1147  			d.Name = finalElem
  1148  			des = append(des, d)
  1149  			// Leave paths empty so we can skip the loop below.
  1150  		} else {
  1151  			// Start with a depth of 0.
  1152  			// A TLF root will have a `finalElem` of "".
  1153  			// A subdirectory will have a `finalElem` of just the name.
  1154  			paths = append(paths, pathStackElem{finalElem, 0})
  1155  		}
  1156  
  1157  		for len(paths) > 0 {
  1158  			// Take last element and shorten.
  1159  			pathElem := paths[len(paths)-1]
  1160  			paths = paths[:len(paths)-1]
  1161  			pathName := ""
  1162  			if pathElem.path != finalElem {
  1163  				pathName = strings.TrimPrefix(pathElem.path, finalElem+"/")
  1164  			}
  1165  
  1166  			fis, err := fs.ReadDir(pathElem.path)
  1167  			if err != nil {
  1168  				return err
  1169  			}
  1170  			linkFS, err := fs.Chroot(pathElem.path)
  1171  			if err != nil {
  1172  				return err
  1173  			}
  1174  			for _, fi := range fis {
  1175  				// We can only get here if we're listing a
  1176  				// directory, not a single file, so we should
  1177  				// always filter.
  1178  				if isFiltered(filter, fi.Name()) {
  1179  					continue
  1180  				}
  1181  
  1182  				var de keybase1.Dirent
  1183  				err := k.setStat(&de, fi, linkFS)
  1184  				if err != nil {
  1185  					return err
  1186  				}
  1187  				de.Name = stdpath.Join(pathName, fi.Name())
  1188  				des = append(des, de)
  1189  				// Only recurse if the caller requested infinite depth (-1), or
  1190  				// if the current path has a depth less than the desired final
  1191  				// depth of recursion.
  1192  				if fi.IsDir() && (finalDepth == -1 || pathElem.depth < finalDepth) {
  1193  					paths = append(paths, pathStackElem{stdpath.Join(pathElem.path, fi.Name()), pathElem.depth + 1})
  1194  				}
  1195  			}
  1196  			k.updateReadProgress(opID, 0, int64(len(fis)))
  1197  		}
  1198  		k.setResult(opID, keybase1.SimpleFSListResult{Entries: des})
  1199  
  1200  		return nil
  1201  	}
  1202  }
  1203  
  1204  // SimpleFSListRecursiveToDepth - Begin recursive list of items in directory at
  1205  // path up to a given depth.
  1206  func (k *SimpleFS) SimpleFSListRecursiveToDepth(
  1207  	ctx context.Context, arg keybase1.SimpleFSListRecursiveToDepthArg) (err error) {
  1208  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_LIST_RECURSIVE_TO_DEPTH,
  1209  		keybase1.NewOpDescriptionWithListRecursiveToDepth(
  1210  			keybase1.ListToDepthArgs{
  1211  				OpID: arg.OpID, Path: arg.Path, Filter: arg.Filter, Depth: arg.Depth,
  1212  			}),
  1213  		&arg.Path, nil,
  1214  		k.listRecursiveToDepth(arg.OpID, arg.Path, arg.Filter, arg.Depth, arg.RefreshSubscription),
  1215  	)
  1216  }
  1217  
  1218  // SimpleFSListRecursive - Begin recursive list of items in directory at path
  1219  func (k *SimpleFS) SimpleFSListRecursive(
  1220  	ctx context.Context, arg keybase1.SimpleFSListRecursiveArg) (err error) {
  1221  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_LIST_RECURSIVE,
  1222  		keybase1.NewOpDescriptionWithListRecursive(
  1223  			keybase1.ListArgs{
  1224  				OpID: arg.OpID, Path: arg.Path, Filter: arg.Filter,
  1225  			}),
  1226  		&arg.Path, nil,
  1227  		k.listRecursiveToDepth(arg.OpID, arg.Path, arg.Filter, -1, arg.RefreshSubscription),
  1228  	)
  1229  }
  1230  
  1231  // SimpleFSReadList - Get list of Paths in progress. Can indicate status of pending
  1232  // to get more entries.
  1233  func (k *SimpleFS) SimpleFSReadList(_ context.Context, opid keybase1.OpID) (keybase1.SimpleFSListResult, error) {
  1234  	k.lock.Lock()
  1235  	res := k.handles[opid]
  1236  	var x interface{}
  1237  	if res != nil {
  1238  		x = res.async
  1239  		res.async = nil
  1240  	}
  1241  	k.lock.Unlock()
  1242  
  1243  	lr, ok := x.(keybase1.SimpleFSListResult)
  1244  	if !ok {
  1245  		return keybase1.SimpleFSListResult{}, errNoResult
  1246  	}
  1247  
  1248  	return lr, nil
  1249  }
  1250  
  1251  // SimpleFSListFavorites lists the favorite, new,
  1252  // and ignored folders of the logged in user,
  1253  // getting its data from the KBFS Favorites cache. If the cache is stale,
  1254  // this will trigger a network request.
  1255  func (k *SimpleFS) SimpleFSListFavorites(ctx context.Context) (
  1256  	keybase1.FavoritesResult, error) {
  1257  	kbpki, err := k.getKBPKI(ctx)
  1258  	if err != nil {
  1259  		return keybase1.FavoritesResult{}, err
  1260  	}
  1261  	session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true)
  1262  	if err != nil {
  1263  		return keybase1.FavoritesResult{}, err
  1264  	}
  1265  	if session.UID.IsNil() {
  1266  		return keybase1.FavoritesResult{}, nil
  1267  	}
  1268  
  1269  	k.config.GetPerfLog().CDebugf(ctx, "GetFavorites simplefs.SimpleFSListFavorites")
  1270  	return k.config.KBFSOps().GetFavoritesAll(ctx)
  1271  }
  1272  
  1273  func recursiveByteAndFileCount(fs billy.Filesystem) (
  1274  	bytes, files int64, err error) {
  1275  	fileInfos, err := fs.ReadDir("/")
  1276  	if err != nil {
  1277  		return 0, 0, err
  1278  	}
  1279  
  1280  	for _, fi := range fileInfos {
  1281  		if fi.IsDir() {
  1282  			if fi.Name() == "." {
  1283  				continue
  1284  			}
  1285  			chrootFS, err := fs.Chroot(fi.Name())
  1286  			if err != nil {
  1287  				return 0, 0, err
  1288  			}
  1289  			chrootBytes, chrootFiles, err := recursiveByteAndFileCount(chrootFS)
  1290  			if err != nil {
  1291  				return 0, 0, err
  1292  			}
  1293  			bytes += chrootBytes
  1294  			files += chrootFiles
  1295  		} else {
  1296  			bytes += fi.Size()
  1297  		}
  1298  		files++
  1299  	}
  1300  	return bytes, files, nil
  1301  }
  1302  
  1303  func copyWithCancellation(ctx context.Context, dst io.Writer, src io.Reader) error {
  1304  	for {
  1305  		select {
  1306  		case <-ctx.Done():
  1307  			return ctx.Err()
  1308  		default:
  1309  		}
  1310  		_, err := io.CopyN(dst, src, 64*1024)
  1311  		if err == io.EOF {
  1312  			return nil
  1313  		}
  1314  		if err != nil {
  1315  			return err
  1316  		}
  1317  	}
  1318  }
  1319  
  1320  type progressReader struct {
  1321  	k     *SimpleFS
  1322  	opID  keybase1.OpID
  1323  	input io.Reader
  1324  }
  1325  
  1326  var _ io.Reader = (*progressReader)(nil)
  1327  
  1328  func (pr *progressReader) Read(p []byte) (n int, err error) {
  1329  	n, err = pr.input.Read(p)
  1330  	if n > 0 {
  1331  		// Update read progress, even for errors.
  1332  		pr.k.updateReadProgress(pr.opID, int64(n), 0)
  1333  	}
  1334  	return n, err
  1335  }
  1336  
  1337  type progressWriter struct {
  1338  	k      *SimpleFS
  1339  	opID   keybase1.OpID
  1340  	output io.Writer
  1341  }
  1342  
  1343  var _ io.Writer = (*progressWriter)(nil)
  1344  
  1345  func (pw *progressWriter) Write(p []byte) (n int, err error) {
  1346  	n, err = pw.output.Write(p)
  1347  	if n > 0 {
  1348  		// Update write progress, even for errors.
  1349  		pw.k.updateWriteProgress(pw.opID, int64(n), 0)
  1350  	}
  1351  	return n, err
  1352  }
  1353  
  1354  func (k *SimpleFS) doCopyFromSource(
  1355  	ctx context.Context, opID keybase1.OpID,
  1356  	srcFS billy.Filesystem, srcFI os.FileInfo,
  1357  	dstPath keybase1.Path, dstFS billy.Filesystem,
  1358  	finalDstElem string, overwriteExistingFiles bool) (err error) {
  1359  	defer func() {
  1360  		if err == nil {
  1361  			k.updateReadProgress(opID, 0, 1)
  1362  			k.updateWriteProgress(opID, 0, 1)
  1363  		}
  1364  	}()
  1365  
  1366  	if srcFI.IsDir() {
  1367  		return dstFS.MkdirAll(finalDstElem, 0755)
  1368  	}
  1369  
  1370  	src, err := srcFS.Open(srcFI.Name())
  1371  	if err != nil {
  1372  		return err
  1373  	}
  1374  	defer src.Close()
  1375  
  1376  	mode := os.O_RDWR | os.O_CREATE | os.O_EXCL
  1377  	if overwriteExistingFiles {
  1378  		mode = os.O_RDWR | os.O_CREATE | os.O_TRUNC
  1379  	}
  1380  	dst, err := dstFS.OpenFile(finalDstElem, mode, 0600)
  1381  	if err != nil {
  1382  		return err
  1383  	}
  1384  	defer dst.Close()
  1385  
  1386  	if pathType, _ := dstPath.PathType(); pathType == keybase1.PathType_LOCAL {
  1387  		defer func() {
  1388  			qerr := Quarantine(ctx, dstPath.Local())
  1389  			if err == nil {
  1390  				err = qerr
  1391  			}
  1392  		}()
  1393  	}
  1394  
  1395  	err = copyWithCancellation(
  1396  		ctx,
  1397  		&progressWriter{k, opID, dst},
  1398  		&progressReader{k, opID, src},
  1399  	)
  1400  	return err
  1401  }
  1402  
  1403  func (k *SimpleFS) doCopy(
  1404  	ctx context.Context, opID keybase1.OpID,
  1405  	srcPath, destPath keybase1.Path, overwriteExistingFiles bool) (err error) {
  1406  	// Note this is also used by move, so if this changes update SimpleFSMove
  1407  	// code also.
  1408  	srcFS, finalSrcElem, err := k.getFS(ctx, srcPath)
  1409  	if err != nil {
  1410  		return err
  1411  	}
  1412  	srcFI, err := srcFS.Stat(finalSrcElem)
  1413  	if err != nil {
  1414  		return err
  1415  	}
  1416  	if srcFI.IsDir() {
  1417  		// The byte count for making a single directory is meaningless.
  1418  		k.setProgressTotals(opID, 0, 1)
  1419  	} else {
  1420  		k.setProgressTotals(opID, srcFI.Size(), 1)
  1421  	}
  1422  	destFS, finalDestElem, err := k.getFS(ctx, destPath)
  1423  	if err != nil {
  1424  		return err
  1425  	}
  1426  
  1427  	return k.doCopyFromSource(ctx, opID,
  1428  		srcFS, srcFI, destPath, destFS, finalDestElem, overwriteExistingFiles)
  1429  }
  1430  
  1431  // SimpleFSCopy - Begin copy of file or directory
  1432  func (k *SimpleFS) SimpleFSCopy(
  1433  	ctx context.Context, arg keybase1.SimpleFSCopyArg) (err error) {
  1434  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_COPY,
  1435  		keybase1.NewOpDescriptionWithCopy(keybase1.CopyArgs(arg)),
  1436  		&arg.Src, &arg.Dest,
  1437  		func(ctx context.Context) (err error) {
  1438  			defer func() { err = translateErr(err) }()
  1439  			return k.doCopy(ctx, arg.OpID, arg.Src, arg.Dest, arg.OverwriteExistingFiles)
  1440  		})
  1441  }
  1442  
  1443  // SimpleFSSymlink starts making a symlink of a file or directory
  1444  func (k *SimpleFS) SimpleFSSymlink(
  1445  	ctx context.Context, arg keybase1.SimpleFSSymlinkArg) (err error) {
  1446  	// This is not async.
  1447  	ctx, err = k.startSyncOp(ctx, "Symlink", arg, &arg.Link, nil)
  1448  	if err != nil {
  1449  		return err
  1450  	}
  1451  	defer func() { k.doneSyncOp(ctx, err) }()
  1452  
  1453  	destFS, finalDestElem, err := k.getFS(ctx, arg.Link)
  1454  	if err != nil {
  1455  		return err
  1456  	}
  1457  
  1458  	err = destFS.Symlink(arg.Target, finalDestElem)
  1459  	return err
  1460  }
  1461  
  1462  type copyNode struct {
  1463  	dest                        keybase1.Path
  1464  	srcFS, destFS               billy.Filesystem
  1465  	srcFinalElem, destFinalElem string
  1466  }
  1467  
  1468  func pathAppend(p keybase1.Path, leaf string) keybase1.Path {
  1469  	switch {
  1470  	case p.Local__ != nil:
  1471  		var s = stdpath.Join(*p.Local__, leaf)
  1472  		p.Local__ = &s
  1473  	case p.Kbfs__ != nil:
  1474  		var s = stdpath.Join(p.Kbfs__.Path, leaf)
  1475  		p = p.DeepCopy()
  1476  		p.Kbfs__.Path = s
  1477  	case p.KbfsArchived__ != nil:
  1478  		var s = stdpath.Join(p.KbfsArchived__.Path, leaf)
  1479  		p = p.DeepCopy()
  1480  		p.KbfsArchived__.Path = s
  1481  	}
  1482  	return p
  1483  }
  1484  
  1485  func (k *SimpleFS) doCopyRecursive(ctx context.Context,
  1486  	opID keybase1.OpID, src, dest keybase1.Path, overwriteExistingFiles bool) error {
  1487  	// Get the full byte/file count.
  1488  	srcFS, finalSrcElem, err := k.getFSIfExists(ctx, src)
  1489  	if err != nil {
  1490  		return err
  1491  	}
  1492  	srcFI, err := srcFS.Stat(finalSrcElem)
  1493  	if err != nil {
  1494  		return err
  1495  	}
  1496  	if srcFI.IsDir() {
  1497  		chrootFS, err := srcFS.Chroot(srcFI.Name())
  1498  		if err != nil {
  1499  			return err
  1500  		}
  1501  		bytes, files, err := recursiveByteAndFileCount(chrootFS)
  1502  		if err != nil {
  1503  			return err
  1504  		}
  1505  		// Add one to files to account for the src dir itself.
  1506  		k.setProgressTotals(opID, bytes, files+1)
  1507  	} else {
  1508  		// No need for recursive.
  1509  		return k.doCopy(ctx, opID, src, dest, overwriteExistingFiles)
  1510  	}
  1511  
  1512  	destFS, finalDestElem, err := k.getFS(ctx, dest)
  1513  	if err != nil {
  1514  		return err
  1515  	}
  1516  
  1517  	var nodes = []copyNode{{
  1518  		dest:          dest,
  1519  		srcFS:         srcFS,
  1520  		destFS:        destFS,
  1521  		srcFinalElem:  finalSrcElem,
  1522  		destFinalElem: finalDestElem,
  1523  	}}
  1524  	for len(nodes) > 0 {
  1525  		select {
  1526  		case <-ctx.Done():
  1527  			return ctx.Err()
  1528  		default:
  1529  		}
  1530  
  1531  		node := nodes[len(nodes)-1]
  1532  		nodes = nodes[:len(nodes)-1]
  1533  
  1534  		srcFI, err := node.srcFS.Stat(node.srcFinalElem)
  1535  		if err != nil {
  1536  			return err
  1537  		}
  1538  
  1539  		err = k.doCopyFromSource(
  1540  			ctx, opID, node.srcFS, srcFI, node.dest, node.destFS,
  1541  			node.destFinalElem, overwriteExistingFiles)
  1542  		if err != nil {
  1543  			return err
  1544  		}
  1545  
  1546  		// TODO symlinks
  1547  		if srcFI.IsDir() {
  1548  			fis, err := node.srcFS.ReadDir(srcFI.Name())
  1549  			if err != nil {
  1550  				return err
  1551  			}
  1552  
  1553  			newSrcFS, err := node.srcFS.Chroot(node.srcFinalElem)
  1554  			if err != nil {
  1555  				return err
  1556  			}
  1557  
  1558  			newDestFS, err := node.destFS.Chroot(node.destFinalElem)
  1559  			if err != nil {
  1560  				return err
  1561  			}
  1562  
  1563  			for _, fi := range fis {
  1564  				name := fi.Name()
  1565  				nodes = append(nodes, copyNode{
  1566  					dest:          pathAppend(node.dest, name),
  1567  					srcFS:         newSrcFS,
  1568  					destFS:        newDestFS,
  1569  					srcFinalElem:  name,
  1570  					destFinalElem: name,
  1571  				})
  1572  			}
  1573  		}
  1574  	}
  1575  	return err
  1576  }
  1577  
  1578  // SimpleFSCopyRecursive - Begin recursive copy of directory
  1579  func (k *SimpleFS) SimpleFSCopyRecursive(ctx context.Context,
  1580  	arg keybase1.SimpleFSCopyRecursiveArg) (err error) {
  1581  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_COPY,
  1582  		keybase1.NewOpDescriptionWithCopy(keybase1.CopyArgs(arg)),
  1583  		&arg.Src, &arg.Dest,
  1584  		func(ctx context.Context) (err error) {
  1585  			defer func() { err = translateErr(err) }()
  1586  			return k.doCopyRecursive(ctx, arg.OpID, arg.Src, arg.Dest, arg.OverwriteExistingFiles)
  1587  		})
  1588  }
  1589  
  1590  func (k *SimpleFS) doRemove(
  1591  	ctx context.Context, path keybase1.Path, recursive bool) error {
  1592  	fs, finalElem, err := k.getFS(ctx, path)
  1593  	if err != nil {
  1594  		return err
  1595  	}
  1596  	if !recursive {
  1597  		if finalElem == "" {
  1598  			// If this is trying to remove a TLF, use favorite removal
  1599  			// instead.
  1600  			if asLibFS, ok := fs.(*libfs.FS); ok {
  1601  				h := asLibFS.Handle()
  1602  				return k.config.KBFSOps().DeleteFavorite(ctx, h.ToFavorite())
  1603  			}
  1604  		}
  1605  		return fs.Remove(finalElem)
  1606  	} else if finalElem == "" {
  1607  		// Give a nice error in the case where we're trying to
  1608  		// recursively delete a TLF.
  1609  		return errors.Errorf("Cannot recursively delete %s", fs.Root())
  1610  	}
  1611  	fi, err := fs.Lstat(finalElem)
  1612  	if err != nil {
  1613  		return err
  1614  	}
  1615  	return libfs.RecursiveDelete(ctx, fs, fi)
  1616  }
  1617  
  1618  func (k *SimpleFS) pathsForSameTlfMove(
  1619  	ctx context.Context, src, dest keybase1.Path) (
  1620  	sameTlf bool, srcPath, destPath string, tlfHandle *tlfhandle.Handle,
  1621  	err error) {
  1622  	srcType, err := src.PathType()
  1623  	if err != nil {
  1624  		return false, "", "", nil, err
  1625  	}
  1626  	if srcType != keybase1.PathType_KBFS {
  1627  		return false, "", "", nil, nil
  1628  	}
  1629  	destType, err := dest.PathType()
  1630  	if err != nil {
  1631  		return false, "", "", nil, err
  1632  	}
  1633  	if destType != keybase1.PathType_KBFS {
  1634  		return false, "", "", nil, nil
  1635  	}
  1636  
  1637  	// They are both KBFS paths -- are they in the same TLF?
  1638  	srcTlfType, srcTlfName, srcMid, srcFinal, err := remoteTlfAndPath(src)
  1639  	if err != nil {
  1640  		return false, "", "", nil, err
  1641  	}
  1642  	destTlfType, destTlfName, destMid, destFinal, err := remoteTlfAndPath(dest)
  1643  	if err != nil {
  1644  		return false, "", "", nil, err
  1645  	}
  1646  	if srcTlfType != destTlfType || srcTlfName != destTlfName {
  1647  		return false, "", "", nil, nil
  1648  	}
  1649  
  1650  	kbpki, err := k.getKBPKI(ctx)
  1651  	if err != nil {
  1652  		return false, "", "", nil, err
  1653  	}
  1654  	tlfHandle, err = libkbfs.GetHandleFromFolderNameAndType(
  1655  		ctx, kbpki, k.config.MDOps(), k.config, srcTlfName,
  1656  		srcTlfType)
  1657  	if err != nil {
  1658  		return false, "", "", nil, err
  1659  	}
  1660  
  1661  	return true, stdpath.Join(srcMid, srcFinal), stdpath.Join(destMid, destFinal),
  1662  		tlfHandle, nil
  1663  }
  1664  
  1665  // SimpleFSMove - Begin move of file or directory, from/to KBFS only
  1666  func (k *SimpleFS) SimpleFSMove(
  1667  	ctx context.Context, arg keybase1.SimpleFSMoveArg) (err error) {
  1668  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_MOVE,
  1669  		keybase1.NewOpDescriptionWithMove(keybase1.MoveArgs(arg)),
  1670  		&arg.Src, &arg.Dest,
  1671  		func(ctx context.Context) (err error) {
  1672  			defer func() { err = translateErr(err) }()
  1673  			sameTlf, srcPath, destPath, tlfHandle, err := k.pathsForSameTlfMove(
  1674  				ctx, arg.Src, arg.Dest)
  1675  			if err != nil {
  1676  				return err
  1677  			}
  1678  			if sameTlf {
  1679  				k.log.CDebugf(ctx, "Renaming within same TLF: %s",
  1680  					tlfHandle.GetCanonicalPath())
  1681  				fs, err := k.newFS(
  1682  					ctx, k.config, tlfHandle, data.MasterBranch, "", false)
  1683  				if err != nil {
  1684  					return err
  1685  				}
  1686  
  1687  				if !arg.OverwriteExistingFiles {
  1688  					// If srcPath is a file, use fs.Create to make a
  1689  					// placeholder to avoid overwriting an existing file. This
  1690  					// doesn't avoid conflicts when journal flushes, but
  1691  					// protects from overwriting over an existing file without
  1692  					// causing conflicts.  We don't need to do it for
  1693  					// directories since libkbfs makes sure we can't rename
  1694  					// over a non-empty directory.
  1695  					fi, err := fs.Stat(srcPath)
  1696  					if err != nil {
  1697  						return err
  1698  					}
  1699  					if !fi.IsDir() {
  1700  						if f, err := fs.OpenFile(destPath,
  1701  							os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600); err != nil {
  1702  							return err
  1703  						} else if err = f.Close(); err != nil {
  1704  							return err
  1705  						}
  1706  					}
  1707  				}
  1708  
  1709  				return fs.Rename(srcPath, destPath)
  1710  			}
  1711  
  1712  			err = k.doCopyRecursive(ctx, arg.OpID, arg.Src, arg.Dest, arg.OverwriteExistingFiles)
  1713  			if err != nil {
  1714  				return err
  1715  			}
  1716  			return k.doRemove(ctx, arg.Src, true)
  1717  		})
  1718  }
  1719  
  1720  func (k *SimpleFS) startSyncOp(
  1721  	ctx context.Context, name string, logarg interface{},
  1722  	path1ForIdentifyBehavior *keybase1.Path,
  1723  	path2ForIdentifyBehavior *keybase1.Path,
  1724  ) (context.Context, error) {
  1725  	ctx = k.makeContext(ctx)
  1726  	ctx, err := populateIdentifyBehaviorIfNeeded(
  1727  		ctx, path1ForIdentifyBehavior, path2ForIdentifyBehavior)
  1728  	if err != nil {
  1729  		return nil, err
  1730  	}
  1731  	k.vlog.CLogf(ctx, libkb.VLog1, "start sync %s %v", name, logarg)
  1732  	return k.startOpWrapContext(ctx)
  1733  }
  1734  func (k *SimpleFS) startOpWrapContext(outer context.Context) (context.Context, error) {
  1735  	return libcontext.NewContextWithCancellationDelayer(libcontext.NewContextReplayable(
  1736  		outer, func(c context.Context) context.Context {
  1737  			return c
  1738  		}))
  1739  }
  1740  
  1741  func (k *SimpleFS) doneSyncOp(ctx context.Context, err error) {
  1742  	k.log.CDebugf(ctx, "done sync op, status=%+v", err)
  1743  	if ctx != nil {
  1744  		err := libcontext.CleanupCancellationDelayer(ctx)
  1745  		if err != nil {
  1746  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  1747  		}
  1748  	}
  1749  }
  1750  
  1751  // SimpleFSRename - Rename file or directory, KBFS side only
  1752  func (k *SimpleFS) SimpleFSRename(
  1753  	ctx context.Context, arg keybase1.SimpleFSRenameArg) (err error) {
  1754  	// This is not async.
  1755  	ctx, err = k.startSyncOp(ctx, "Rename", arg, &arg.Src, &arg.Dest)
  1756  	if err != nil {
  1757  		return err
  1758  	}
  1759  	defer func() { k.doneSyncOp(ctx, err) }()
  1760  
  1761  	// Get root FS, to be shared by both src and dest.
  1762  	t, tlfName, restOfSrcPath, finalSrcElem, err := remoteTlfAndPath(arg.Src)
  1763  	if err != nil {
  1764  		return err
  1765  	}
  1766  	kbpki, err := k.getKBPKI(ctx)
  1767  	if err != nil {
  1768  		return err
  1769  	}
  1770  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  1771  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  1772  	if err != nil {
  1773  		return err
  1774  	}
  1775  	fs, err := k.newFS(
  1776  		ctx, k.config, tlfHandle, data.MasterBranch, "", false)
  1777  	if err != nil {
  1778  		return err
  1779  	}
  1780  
  1781  	// Make sure src and dest share the same TLF.
  1782  	tDest, tlfNameDest, restOfDestPath, finalDestElem, err :=
  1783  		remoteTlfAndPath(arg.Dest)
  1784  	if err != nil {
  1785  		return err
  1786  	}
  1787  	if tDest != t || tlfName != tlfNameDest {
  1788  		return simpleFSError{reason: "Cannot rename across top-level folders"}
  1789  	}
  1790  
  1791  	err = fs.Rename(
  1792  		stdpath.Join(restOfSrcPath, finalSrcElem),
  1793  		stdpath.Join(restOfDestPath, finalDestElem))
  1794  	return err
  1795  }
  1796  
  1797  // SimpleFSOpen - Create/open a file and leave it open
  1798  // or create a directory
  1799  // Files must be closed afterwards.
  1800  func (k *SimpleFS) SimpleFSOpen(
  1801  	ctx context.Context, arg keybase1.SimpleFSOpenArg) (err error) {
  1802  	ctx, err = k.startSyncOp(ctx, "Open", arg, &arg.Dest, nil)
  1803  	if err != nil {
  1804  		return err
  1805  	}
  1806  	defer func() { k.doneSyncOp(ctx, err) }()
  1807  
  1808  	fs, finalElem, err := k.getFS(ctx, arg.Dest)
  1809  	if err != nil {
  1810  		return err
  1811  	}
  1812  
  1813  	// Make a directory if needed.  This will return `nil` if the
  1814  	// directory already exists.
  1815  	if arg.Flags&keybase1.OpenFlags_DIRECTORY != 0 {
  1816  		return fs.MkdirAll(finalElem, 0755)
  1817  	}
  1818  
  1819  	var cflags = os.O_RDONLY
  1820  	// This must be first since it writes the flag, not just ORs into it.
  1821  	if arg.Flags&keybase1.OpenFlags_WRITE != 0 {
  1822  		cflags = os.O_RDWR
  1823  	}
  1824  	if arg.Flags&keybase1.OpenFlags_EXISTING == 0 {
  1825  		cflags |= os.O_CREATE
  1826  	}
  1827  	if arg.Flags&keybase1.OpenFlags_REPLACE != 0 {
  1828  		cflags |= os.O_TRUNC
  1829  	}
  1830  
  1831  	var cancel context.CancelFunc = func() {}
  1832  	if libfs, ok := fs.(*libfs.FS); ok {
  1833  		var fsCtx context.Context
  1834  		fsCtx, cancel = context.WithCancel(k.makeContext(context.Background()))
  1835  		fsCtx, err := k.startOpWrapContext(fsCtx)
  1836  		if err != nil {
  1837  			return err
  1838  		}
  1839  		libfs = libfs.WithContext(fsCtx)
  1840  		k.log.CDebugf(ctx, "New background context for open: SFSID=%s, OpID=%X",
  1841  			fsCtx.Value(ctxIDKey), arg.OpID)
  1842  		fs = libfs
  1843  	}
  1844  
  1845  	f, err := fs.OpenFile(finalElem, cflags, 0644)
  1846  	if err != nil {
  1847  		return err
  1848  	}
  1849  
  1850  	k.lock.Lock()
  1851  	k.handles[arg.OpID] = &handle{file: f, path: arg.Dest, cancel: cancel}
  1852  	k.lock.Unlock()
  1853  
  1854  	return nil
  1855  }
  1856  
  1857  // SimpleFSSetStat - Set/clear file bits - only executable for now
  1858  func (k *SimpleFS) SimpleFSSetStat(
  1859  	ctx context.Context, arg keybase1.SimpleFSSetStatArg) (err error) {
  1860  	ctx, err = k.startSyncOp(ctx, "SetStat", arg, &arg.Dest, nil)
  1861  	if err != nil {
  1862  		return err
  1863  	}
  1864  	defer func() { k.doneSyncOp(ctx, err) }()
  1865  
  1866  	fs, finalElem, err := k.getFS(ctx, arg.Dest)
  1867  	if err != nil {
  1868  		return err
  1869  	}
  1870  	fi, err := fs.Lstat(finalElem)
  1871  	if err != nil {
  1872  		return err
  1873  	}
  1874  
  1875  	mode := fi.Mode()
  1876  	switch arg.Flag {
  1877  	case keybase1.DirentType_EXEC:
  1878  		mode |= 0100
  1879  	case keybase1.DirentType_FILE:
  1880  		mode &= 0677
  1881  	default:
  1882  		return nil
  1883  	}
  1884  
  1885  	changeFS, ok := fs.(billy.Change)
  1886  	if !ok {
  1887  		panic(fmt.Sprintf("Unexpected non-Change FS: %T", fs))
  1888  	}
  1889  
  1890  	return changeFS.Chmod(finalElem, mode)
  1891  }
  1892  
  1893  func (k *SimpleFS) startReadWriteOp(
  1894  	ctx context.Context, opid keybase1.OpID, opType keybase1.AsyncOps,
  1895  	desc keybase1.OpDescription) (context.Context, error) {
  1896  	ctx, err := k.startSyncOp(ctx, desc.AsyncOp__.String(), desc, nil, nil)
  1897  	if err != nil {
  1898  		return nil, err
  1899  	}
  1900  	k.lock.Lock()
  1901  	k.inProgress[opid] = &inprogress{
  1902  		desc,
  1903  		func() {},
  1904  		make(chan error, 1),
  1905  		keybase1.OpProgress{OpType: opType},
  1906  	}
  1907  	k.lock.Unlock()
  1908  	return ctx, err
  1909  }
  1910  
  1911  func (k *SimpleFS) doneReadWriteOp(ctx context.Context, opID keybase1.OpID, err error) {
  1912  	k.lock.Lock()
  1913  	// Read/write ops never set the end estimate since the progress is
  1914  	// just deleted immediately.
  1915  	delete(k.inProgress, opID)
  1916  	k.lock.Unlock()
  1917  	k.log.CDebugf(ctx, "doneReadWriteOp, status=%v", err)
  1918  	if ctx != nil {
  1919  		err := libcontext.CleanupCancellationDelayer(ctx)
  1920  		if err != nil {
  1921  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  1922  		}
  1923  	}
  1924  }
  1925  
  1926  // SimpleFSRead - Read (possibly partial) contents of open file,
  1927  // up to the amount specified by size.
  1928  // Repeat until zero bytes are returned or error.
  1929  // If size is zero, read an arbitrary amount.
  1930  func (k *SimpleFS) SimpleFSRead(ctx context.Context,
  1931  	arg keybase1.SimpleFSReadArg) (_ keybase1.FileContent, err error) {
  1932  	ctx = k.makeContext(ctx)
  1933  	k.lock.RLock()
  1934  	h, ok := k.handles[arg.OpID]
  1935  	k.lock.RUnlock()
  1936  	if !ok {
  1937  		return keybase1.FileContent{}, errNoSuchHandle
  1938  	}
  1939  	opDesc := keybase1.NewOpDescriptionWithRead(
  1940  		keybase1.ReadArgs{
  1941  			OpID:   arg.OpID,
  1942  			Path:   h.path,
  1943  			Offset: arg.Offset,
  1944  			Size:   arg.Size,
  1945  		})
  1946  	ctx, err = k.startReadWriteOp(ctx, arg.OpID, keybase1.AsyncOps_READ, opDesc)
  1947  	if err != nil {
  1948  		return keybase1.FileContent{}, err
  1949  	}
  1950  	k.setProgressTotals(arg.OpID, int64(arg.Size), 1)
  1951  	defer func() {
  1952  		if err == nil {
  1953  			k.updateReadProgress(arg.OpID, 0, 1)
  1954  		}
  1955  	}()
  1956  
  1957  	defer func() { k.doneReadWriteOp(ctx, arg.OpID, err) }()
  1958  
  1959  	// Print this so we can correlate the ID in
  1960  	k.log.CDebugf(ctx, "Starting read for OpID=%X, offset=%d, size=%d",
  1961  		arg.OpID, arg.Offset, arg.Size)
  1962  
  1963  	_, err = h.file.Seek(arg.Offset, io.SeekStart)
  1964  	if err != nil {
  1965  		return keybase1.FileContent{}, err
  1966  	}
  1967  
  1968  	bs := make([]byte, arg.Size)
  1969  	// TODO: make this a proper buffered read so we can get finer progress?
  1970  	reader := &progressReader{k, arg.OpID, h.file}
  1971  	n, err := reader.Read(bs)
  1972  	if err != nil && err != io.EOF {
  1973  		return keybase1.FileContent{}, err
  1974  	}
  1975  	bs = bs[:n]
  1976  	return keybase1.FileContent{
  1977  		Data: bs,
  1978  	}, nil
  1979  }
  1980  
  1981  // SimpleFSWrite - Append content to opened file.
  1982  // May be repeated until OpID is closed.
  1983  func (k *SimpleFS) SimpleFSWrite(
  1984  	ctx context.Context, arg keybase1.SimpleFSWriteArg) (err error) {
  1985  	ctx = k.makeContext(ctx)
  1986  	k.lock.RLock()
  1987  	h, ok := k.handles[arg.OpID]
  1988  	k.lock.RUnlock()
  1989  	if !ok {
  1990  		return errNoSuchHandle
  1991  	}
  1992  
  1993  	opDesc := keybase1.NewOpDescriptionWithWrite(
  1994  		keybase1.WriteArgs{
  1995  			OpID: arg.OpID, Path: h.path, Offset: arg.Offset,
  1996  		})
  1997  
  1998  	ctx, err = k.startReadWriteOp(
  1999  		ctx, arg.OpID, keybase1.AsyncOps_WRITE, opDesc)
  2000  	if err != nil {
  2001  		return err
  2002  	}
  2003  	defer func() { k.doneReadWriteOp(ctx, arg.OpID, err) }()
  2004  
  2005  	k.setProgressTotals(arg.OpID, int64(len(arg.Content)), 1)
  2006  	defer func() {
  2007  		if err == nil {
  2008  			k.updateWriteProgress(arg.OpID, 0, 1)
  2009  		}
  2010  	}()
  2011  
  2012  	k.log.CDebugf(ctx, "Starting write for OpID=%X, offset=%d, size=%d",
  2013  		arg.OpID, arg.Offset, len(arg.Content))
  2014  
  2015  	_, err = h.file.Seek(arg.Offset, io.SeekStart)
  2016  	if err != nil {
  2017  		return err
  2018  	}
  2019  
  2020  	writer := &progressWriter{k, arg.OpID, h.file}
  2021  	_, err = writer.Write(arg.Content)
  2022  	return err
  2023  }
  2024  
  2025  // SimpleFSRemove - Remove file or directory from filesystem
  2026  func (k *SimpleFS) SimpleFSRemove(ctx context.Context,
  2027  	arg keybase1.SimpleFSRemoveArg) (err error) {
  2028  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_REMOVE,
  2029  		keybase1.NewOpDescriptionWithRemove(keybase1.RemoveArgs(arg)),
  2030  		&arg.Path,
  2031  		nil,
  2032  		func(ctx context.Context) (err error) {
  2033  			return k.doRemove(ctx, arg.Path, arg.Recursive)
  2034  		})
  2035  }
  2036  
  2037  // SimpleFSStat - Get info about file
  2038  func (k *SimpleFS) SimpleFSStat(ctx context.Context, arg keybase1.SimpleFSStatArg) (de keybase1.Dirent, err error) {
  2039  	defer func() { err = translateErr(err) }()
  2040  	ctx, err = k.startSyncOp(ctx, "Stat", arg.Path, &arg.Path, nil)
  2041  	if err != nil {
  2042  		return keybase1.Dirent{}, err
  2043  	}
  2044  	defer func() { k.doneSyncOp(ctx, err) }()
  2045  
  2046  	fs, finalElem, err := k.getFSIfExists(ctx, arg.Path)
  2047  	switch errors.Cause(err).(type) {
  2048  	case nil:
  2049  	case libfs.TlfDoesNotExist:
  2050  		k.log.CDebugf(ctx, "Return err for finalElem=%s", finalElem)
  2051  		if finalElem != "" && finalElem != "." {
  2052  			return keybase1.Dirent{}, err
  2053  		}
  2054  
  2055  		// TLF doesn't exist yet; just return an empty result.
  2056  		return keybase1.Dirent{
  2057  			DirentType: keybase1.DirentType_DIR,
  2058  			Writable:   false,
  2059  		}, nil
  2060  	default:
  2061  		return keybase1.Dirent{}, err
  2062  	}
  2063  
  2064  	if arg.RefreshSubscription {
  2065  		err = k.refreshSubscription(ctx, arg.Path)
  2066  		if err != nil {
  2067  			return keybase1.Dirent{}, err
  2068  		}
  2069  	}
  2070  
  2071  	// Use LStat so we don't follow symlinks.
  2072  	fi, err := fs.Lstat(finalElem)
  2073  	if err != nil {
  2074  		return keybase1.Dirent{}, err
  2075  	}
  2076  
  2077  	err = k.setStat(&de, fi, fs)
  2078  	return de, err
  2079  }
  2080  
  2081  func (k *SimpleFS) getRevisionsFromPath(
  2082  	ctx context.Context, path keybase1.Path) (
  2083  	billy.Filesystem, os.FileInfo, data.PrevRevisions, error) {
  2084  	fs, finalElem, err := k.getFSIfExists(ctx, path)
  2085  	if err != nil {
  2086  		k.log.CDebugf(ctx, "Trouble getting fs for path: %+v", err)
  2087  		return nil, nil, nil, err
  2088  	}
  2089  	// Use LStat so we don't follow symlinks.
  2090  	fi, err := fs.Lstat(finalElem)
  2091  	if err != nil {
  2092  		return nil, nil, nil, err
  2093  	}
  2094  
  2095  	fipr, ok := fi.Sys().(libfs.PrevRevisionsGetter)
  2096  	if !ok {
  2097  		return nil, nil, nil,
  2098  			simpleFSError{reason: "Cannot get revisions for non-KBFS path"}
  2099  	}
  2100  	return fs, fi, fipr.PrevRevisions(), nil
  2101  }
  2102  
  2103  func (k *SimpleFS) doGetRevisions(
  2104  	ctx context.Context, opID keybase1.OpID, path keybase1.Path,
  2105  	spanType keybase1.RevisionSpanType) (
  2106  	revs []keybase1.DirentWithRevision, err error) {
  2107  	k.vlog.CLogf(ctx, libkb.VLog1, "Getting revisions for path %s, spanType=%s",
  2108  		path, spanType)
  2109  
  2110  	// Both span types return up to 5 revisions.
  2111  	k.setProgressTotals(opID, 0, 5)
  2112  
  2113  	fs, fi, prs, err := k.getRevisionsFromPath(ctx, path)
  2114  	if err != nil {
  2115  		return nil, err
  2116  	}
  2117  	if len(prs) == 0 {
  2118  		return nil, simpleFSError{reason: "No previous revisions"}
  2119  	}
  2120  
  2121  	var currRev keybase1.DirentWithRevision
  2122  	err = k.setStat(&currRev.Entry, fi, fs)
  2123  	if err != nil {
  2124  		return nil, err
  2125  	}
  2126  	currRev.Revision = keybase1.KBFSRevision(prs[0].Revision)
  2127  	k.log.CDebugf(ctx, "Found current revision: %d", prs[0].Revision)
  2128  	k.updateReadProgress(opID, 0, 1)
  2129  
  2130  	var revPaths []keybase1.Path
  2131  
  2132  	// The next four depend on the span type.
  2133  	pathStr := path.String()
  2134  	switch spanType {
  2135  	case keybase1.RevisionSpanType_DEFAULT:
  2136  		// Use `prs` for the rest of the paths.
  2137  		for i := 1; i < len(prs); i++ {
  2138  			p := keybase1.NewPathWithKbfsArchived(keybase1.KBFSArchivedPath{
  2139  				Path: pathStr,
  2140  				ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision(
  2141  					keybase1.KBFSRevision(prs[i].Revision)),
  2142  			})
  2143  			revPaths = append(revPaths, p)
  2144  		}
  2145  	case keybase1.RevisionSpanType_LAST_FIVE:
  2146  		expectedCount := uint8(2)
  2147  		nextSlot := 1
  2148  		lastRevision := prs[0].Revision
  2149  
  2150  		// Step back through the previous revisions.  If the next one
  2151  		// in the list happens to be the next in line (because the
  2152  		// count is one more than the current count), use it.
  2153  		// Otherwise, we have to fetch the stats from the MD revision
  2154  		// before the last one we processed, and use the
  2155  		// PreviousRevisions list from that version of the file.
  2156  		for len(revPaths) < 4 && nextSlot < len(prs) {
  2157  			var rev kbfsmd.Revision
  2158  			switch {
  2159  			case prs[nextSlot].Count == expectedCount:
  2160  				rev = prs[nextSlot].Revision
  2161  			case lastRevision > kbfsmd.RevisionInitial:
  2162  				k.log.CDebugf(ctx, "Inspecting revision %d to find previous",
  2163  					lastRevision-1)
  2164  				pathToPrev := keybase1.NewPathWithKbfsArchived(
  2165  					keybase1.KBFSArchivedPath{
  2166  						Path: pathStr,
  2167  						ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision(
  2168  							keybase1.KBFSRevision(lastRevision - 1)),
  2169  					})
  2170  				_, _, prevPRs, err := k.getRevisionsFromPath(ctx, pathToPrev)
  2171  				if _, isGC := err.(libkbfs.RevGarbageCollectedError); isGC {
  2172  					k.log.CDebugf(ctx, "Hit a GC'd revision: %d",
  2173  						lastRevision-1)
  2174  					break
  2175  				} else if err != nil {
  2176  					return nil, err
  2177  				}
  2178  				if len(prevPRs) == 0 {
  2179  					// This should never happen, because there is some
  2180  					// next slot in the `prs` list, but it doesn't
  2181  					// match the expected count, which means there
  2182  					// must be _some_ revision in between the last
  2183  					// revision and the one in the next slot, that we
  2184  					// should uncover by looking up `lastRevision-1`.
  2185  					return nil, simpleFSError{reason: fmt.Sprintf(
  2186  						"Revision %s unexpectedly lists no previous revisions",
  2187  						lastRevision-1)}
  2188  				}
  2189  				rev = prevPRs[0].Revision
  2190  				prs = prevPRs
  2191  				nextSlot = 0      // will be incremented below
  2192  				expectedCount = 1 // will be incremented below
  2193  			default:
  2194  				break
  2195  			}
  2196  
  2197  			p := keybase1.NewPathWithKbfsArchived(keybase1.KBFSArchivedPath{
  2198  				Path: pathStr,
  2199  				ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision(
  2200  					keybase1.KBFSRevision(rev)),
  2201  			})
  2202  			revPaths = append(revPaths, p)
  2203  			lastRevision = rev
  2204  			nextSlot++
  2205  			expectedCount++
  2206  		}
  2207  	default:
  2208  		return nil, simpleFSError{reason: fmt.Sprintf("Unknown span type: %s", spanType)}
  2209  	}
  2210  
  2211  	if len(revPaths) < 4 {
  2212  		// See if the final revision has a predecessor that's
  2213  		// still live, to fill out the list of 5.  An older
  2214  		// revision could have slid off the previous revisions
  2215  		// list because that revision was garbage-collected, but
  2216  		// that doesn't guarantee that the older revision of the
  2217  		// file was garabge-collected too (since it was created,
  2218  		// not deleted, as of that garbage-collected revision).
  2219  		p := keybase1.NewPathWithKbfsArchived(keybase1.KBFSArchivedPath{
  2220  			Path: pathStr,
  2221  			ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision(
  2222  				keybase1.KBFSRevision(prs[len(prs)-1].Revision - 1)),
  2223  		})
  2224  		revPaths = append(revPaths, p)
  2225  	}
  2226  
  2227  	// Now that we have all the paths we need, stat them one-by-one.
  2228  	revs = make([]keybase1.DirentWithRevision, len(revPaths)+1)
  2229  	revs[0] = currRev
  2230  
  2231  	if len(revs) < 5 {
  2232  		// Discount the revisions that don't exist from the progress.
  2233  		k.updateReadProgress(opID, 0, int64(5-len(revs)))
  2234  	}
  2235  
  2236  	// Fetch all the past revisions in parallel to populate the
  2237  	// directory entry.
  2238  	eg, groupCtx := errgroup.WithContext(ctx)
  2239  	doStat := func(slot int) error {
  2240  		p := revPaths[slot]
  2241  		fs, finalElem, err := k.getFSIfExists(groupCtx, p)
  2242  		if _, isGC := err.(libkbfs.RevGarbageCollectedError); isGC {
  2243  			k.log.CDebugf(ctx, "Hit a GC'd revision: %d",
  2244  				p.KbfsArchived().ArchivedParam.Revision())
  2245  			return nil
  2246  		} else if err != nil {
  2247  			return err
  2248  		}
  2249  		// Use LStat so we don't follow symlinks.
  2250  		fi, err := fs.Lstat(finalElem)
  2251  		if os.IsNotExist(err) {
  2252  			k.log.CDebugf(ctx, "Ran out of revisions as of %d",
  2253  				p.KbfsArchived().ArchivedParam.Revision())
  2254  			return nil
  2255  		}
  2256  		if err != nil {
  2257  			return err
  2258  		}
  2259  		var rev keybase1.DirentWithRevision
  2260  		err = k.setStat(&rev.Entry, fi, fs)
  2261  		if err != nil {
  2262  			return err
  2263  		}
  2264  		rev.Revision = p.KbfsArchived().ArchivedParam.Revision()
  2265  		revs[slot+1] = rev
  2266  		k.updateReadProgress(opID, 0, 1)
  2267  		return nil
  2268  	}
  2269  	for i := range revPaths {
  2270  		i := i
  2271  		eg.Go(func() error { return doStat(i) })
  2272  	}
  2273  	err = eg.Wait()
  2274  	if err != nil {
  2275  		return nil, err
  2276  	}
  2277  
  2278  	// Remove any GC'd revisions.
  2279  	for i, r := range revs {
  2280  		if kbfsmd.Revision(r.Revision) == kbfsmd.RevisionUninitialized {
  2281  			revs = revs[:i]
  2282  			break
  2283  		}
  2284  	}
  2285  
  2286  	return revs, nil
  2287  }
  2288  
  2289  // SimpleFSGetRevisions - Get revisions for a file
  2290  func (k *SimpleFS) SimpleFSGetRevisions(
  2291  	ctx context.Context, arg keybase1.SimpleFSGetRevisionsArg) (err error) {
  2292  	return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_GET_REVISIONS,
  2293  		keybase1.NewOpDescriptionWithGetRevisions(
  2294  			keybase1.GetRevisionsArgs(arg)),
  2295  		&arg.Path,
  2296  		nil,
  2297  		func(ctx context.Context) (err error) {
  2298  			revs, err := k.doGetRevisions(ctx, arg.OpID, arg.Path, arg.SpanType)
  2299  			if err != nil {
  2300  				return err
  2301  			}
  2302  			k.setResult(arg.OpID, keybase1.GetRevisionsResult{
  2303  				Revisions: revs,
  2304  				// For don't set any progress indicators.  If we decide we want
  2305  				// to display partial results, we can fix this later.
  2306  			})
  2307  			return nil
  2308  		})
  2309  }
  2310  
  2311  // SimpleFSReadRevisions - Get list of revisions in progress. Can
  2312  // indicate status of pending to get more revisions.
  2313  func (k *SimpleFS) SimpleFSReadRevisions(
  2314  	_ context.Context, opid keybase1.OpID) (
  2315  	keybase1.GetRevisionsResult, error) {
  2316  	k.lock.Lock()
  2317  	res := k.handles[opid]
  2318  	var x interface{}
  2319  	if res != nil {
  2320  		x = res.async
  2321  		res.async = nil
  2322  	}
  2323  	k.lock.Unlock()
  2324  
  2325  	lr, ok := x.(keybase1.GetRevisionsResult)
  2326  	if !ok {
  2327  		return keybase1.GetRevisionsResult{}, errNoResult
  2328  	}
  2329  
  2330  	return lr, nil
  2331  }
  2332  
  2333  // SimpleFSMakeOpid - Convenience helper for generating new random value
  2334  func (k *SimpleFS) SimpleFSMakeOpid(_ context.Context) (keybase1.OpID, error) {
  2335  	var opid keybase1.OpID
  2336  	err := kbfscrypto.RandRead(opid[:])
  2337  	return opid, err
  2338  }
  2339  
  2340  // SimpleFSClose - Close removes a handle associated with Open / List.
  2341  func (k *SimpleFS) SimpleFSClose(ctx context.Context, opid keybase1.OpID) (err error) {
  2342  	ctx, err = k.startSyncOp(ctx, "Close", opid, nil, nil)
  2343  	if err != nil {
  2344  		return err
  2345  	}
  2346  	defer func() { k.doneSyncOp(ctx, err) }()
  2347  
  2348  	k.lock.Lock()
  2349  	defer k.lock.Unlock()
  2350  	delete(k.inProgress, opid)
  2351  	h, ok := k.handles[opid]
  2352  	if !ok {
  2353  		return errNoSuchHandle
  2354  	}
  2355  	delete(k.handles, opid)
  2356  	if h.file != nil {
  2357  		err = h.file.Close()
  2358  	}
  2359  	if h.cancel != nil {
  2360  		h.cancel()
  2361  	}
  2362  	return err
  2363  }
  2364  
  2365  // SimpleFSCancel starts to cancel op with the given opid.
  2366  // Also remove any pending references of opid everywhere.
  2367  // Returns before cancellation is guaranteeded to be done - that
  2368  // may take some time. Currently always returns nil.
  2369  func (k *SimpleFS) SimpleFSCancel(_ context.Context, opid keybase1.OpID) error {
  2370  	k.lock.Lock()
  2371  	defer k.lock.Unlock()
  2372  	delete(k.handles, opid)
  2373  	w, ok := k.inProgress[opid]
  2374  	if !ok {
  2375  		return nil
  2376  	}
  2377  	delete(k.inProgress, opid)
  2378  	w.cancel()
  2379  	return nil
  2380  }
  2381  
  2382  // SimpleFSCheck - Check progress of pending operation
  2383  // Progress variable is still TBD.
  2384  // Return errNoResult if no operation found.
  2385  func (k *SimpleFS) SimpleFSCheck(
  2386  	ctx context.Context, opid keybase1.OpID) (keybase1.OpProgress, error) {
  2387  	k.lock.RLock()
  2388  	defer k.lock.RUnlock()
  2389  	if p, ok := k.inProgress[opid]; ok {
  2390  		// For now, estimate the ending time purely on the read progress.
  2391  		var n, d int64
  2392  		progress := p.progress
  2393  		if progress.BytesTotal > 0 {
  2394  			n = progress.BytesRead
  2395  			d = progress.BytesTotal
  2396  		} else if p.progress.FilesTotal > 0 {
  2397  			n = progress.FilesRead
  2398  			d = progress.FilesTotal
  2399  		}
  2400  		if n > 0 && d > 0 && !progress.Start.IsZero() &&
  2401  			progress.EndEstimate.IsZero() {
  2402  			// Crudely estimate that the total time for the op is the
  2403  			// time spent so far, divided by the fraction of the
  2404  			// reading that's been done.
  2405  			start := keybase1.FromTime(progress.Start)
  2406  			timeRunning := k.config.Clock().Now().Sub(start)
  2407  			fracDone := float64(n) / float64(d)
  2408  			totalTimeEstimate := time.Duration(float64(timeRunning) / fracDone)
  2409  			progress.EndEstimate =
  2410  				keybase1.ToTime(start.Add(totalTimeEstimate))
  2411  			k.log.CDebugf(ctx, "Start=%s, n=%d, d=%d, fracDone=%f, End=%s",
  2412  				start, n, d, fracDone, start.Add(totalTimeEstimate))
  2413  		}
  2414  
  2415  		return progress, nil
  2416  	} else if _, ok := k.handles[opid]; ok {
  2417  		// Return an empty progress and nil error if there's no async
  2418  		// operation pending, but there is still an open handle.
  2419  		return keybase1.OpProgress{}, nil
  2420  	}
  2421  	return keybase1.OpProgress{}, errNoResult
  2422  }
  2423  
  2424  // SimpleFSGetOps - Get all the outstanding operations
  2425  func (k *SimpleFS) SimpleFSGetOps(_ context.Context) ([]keybase1.OpDescription, error) {
  2426  	k.lock.RLock()
  2427  	r := make([]keybase1.OpDescription, 0, len(k.inProgress))
  2428  	for _, p := range k.inProgress {
  2429  		r = append(r, p.desc)
  2430  	}
  2431  	k.lock.RUnlock()
  2432  	return r, nil
  2433  }
  2434  
  2435  // SimpleFSWait - Blocking wait for the pending operation to finish
  2436  func (k *SimpleFS) SimpleFSWait(ctx context.Context, opid keybase1.OpID) error {
  2437  	ctx = k.makeContext(ctx)
  2438  	k.lock.RLock()
  2439  	w, ok := k.inProgress[opid]
  2440  	k.log.CDebugf(ctx, "Wait %X -> %v, %v", opid, w, ok)
  2441  	k.lock.RUnlock()
  2442  	if !ok {
  2443  		return errNoSuchHandle
  2444  	}
  2445  
  2446  	err, ok := <-w.done
  2447  
  2448  	k.lock.Lock()
  2449  	delete(k.inProgress, opid)
  2450  	k.lock.Unlock()
  2451  
  2452  	if !ok {
  2453  		return errNoResult
  2454  	}
  2455  	return err
  2456  }
  2457  
  2458  // SimpleFSDumpDebuggingInfo - Instructs KBFS to dump debugging info
  2459  // into its logs.
  2460  func (k *SimpleFS) SimpleFSDumpDebuggingInfo(ctx context.Context) error {
  2461  	ctx = k.makeContext(ctx)
  2462  	k.idd.ForceDump(ctx)
  2463  	return nil
  2464  }
  2465  
  2466  // This timeout needs to be smaller than the one in
  2467  // keybase/client/go/service/simplefs.go so that for situations where error is
  2468  // not critical (e.g. quota usage in journal status calls), we'll get (and
  2469  // ignore) a timeout before service times us out in RPC.
  2470  const simpleFSFastActionTimeout = 6 * time.Second
  2471  
  2472  // SimpleFSSyncStatus - Get sync status.
  2473  func (k *SimpleFS) SimpleFSSyncStatus(ctx context.Context, filter keybase1.ListFilter) (keybase1.FSSyncStatus, error) {
  2474  	ctx, cancel := context.WithTimeout(
  2475  		k.makeContext(ctx), simpleFSFastActionTimeout)
  2476  	defer cancel()
  2477  	jManager, jErr := libkbfs.GetJournalManager(k.config)
  2478  	if jErr != nil {
  2479  		k.log.CDebugf(ctx, "Journal not enabled; sending empty response")
  2480  		return keybase1.FSSyncStatus{}, nil
  2481  	}
  2482  	status, tlfIDs := jManager.Status(ctx)
  2483  	err := libkbfs.FillInJournalStatusUnflushedPaths(
  2484  		ctx, k.config, &status, tlfIDs)
  2485  	if err != nil {
  2486  		k.log.CDebugf(ctx, "Error setting unflushed paths: %+v; "+
  2487  			"sending empty response", err)
  2488  		return keybase1.FSSyncStatus{}, nil
  2489  	}
  2490  
  2491  	var syncingPaths []string
  2492  	if filter == keybase1.ListFilter_NO_FILTER {
  2493  		syncingPaths = status.UnflushedPaths
  2494  	} else {
  2495  		for _, p := range status.UnflushedPaths {
  2496  
  2497  			if isFiltered(filter, stdpath.Base(p)) {
  2498  				continue
  2499  			}
  2500  			syncingPaths = append(syncingPaths, p)
  2501  		}
  2502  	}
  2503  
  2504  	k.log.CDebugf(ctx, "Sending sync status response with %d syncing bytes",
  2505  		status.UnflushedBytes)
  2506  	return keybase1.FSSyncStatus{
  2507  		TotalSyncingBytes: status.UnflushedBytes,
  2508  		SyncingPaths:      syncingPaths,
  2509  		EndEstimate:       keybase1.ToTimePtr(status.EndEstimate),
  2510  	}, nil
  2511  }
  2512  
  2513  // SimpleFSUserEditHistory returns the edit history for the logged-in user.
  2514  func (k *SimpleFS) SimpleFSUserEditHistory(ctx context.Context) (
  2515  	res []keybase1.FSFolderEditHistory, err error) {
  2516  	kbpki, err := k.getKBPKI(ctx)
  2517  	if err != nil {
  2518  		return nil, err
  2519  	}
  2520  	session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true)
  2521  	// Return empty history if we are not logged in.
  2522  	if err != nil {
  2523  		return nil, nil
  2524  	}
  2525  	return k.config.UserHistory().Get(string(session.Name)), nil
  2526  }
  2527  
  2528  // SimpleFSFolderEditHistory returns the edit history for the given TLF.
  2529  func (k *SimpleFS) SimpleFSFolderEditHistory(
  2530  	ctx context.Context, path keybase1.Path) (
  2531  	res keybase1.FSFolderEditHistory, err error) {
  2532  	ctx = k.makeContext(ctx)
  2533  	fb, _, err := k.getFolderBranchFromPath(ctx, path)
  2534  	if err != nil {
  2535  		return keybase1.FSFolderEditHistory{}, err
  2536  	}
  2537  	if fb == (data.FolderBranch{}) {
  2538  		return keybase1.FSFolderEditHistory{}, nil
  2539  	}
  2540  
  2541  	// Now get the edit history.
  2542  	return k.config.KBFSOps().GetEditHistory(ctx, fb)
  2543  }
  2544  
  2545  // SimpleFSReset resets the given TLF.
  2546  func (k *SimpleFS) SimpleFSReset(
  2547  	ctx context.Context, arg keybase1.SimpleFSResetArg) error {
  2548  	t, tlfName, _, _, err := remoteTlfAndPath(arg.Path)
  2549  	if err != nil {
  2550  		return err
  2551  	}
  2552  	kbpki, err := k.getKBPKI(ctx)
  2553  	if err != nil {
  2554  		return err
  2555  	}
  2556  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  2557  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  2558  	if err != nil {
  2559  		return err
  2560  	}
  2561  
  2562  	var newTlfID *tlf.ID
  2563  	if arg.TlfID != "" {
  2564  		tlfID, err := tlf.ParseID(arg.TlfID)
  2565  		if err != nil {
  2566  			return err
  2567  		}
  2568  		newTlfID = &tlfID
  2569  	}
  2570  
  2571  	return k.config.KBFSOps().Reset(ctx, tlfHandle, newTlfID)
  2572  }
  2573  
  2574  var _ libkbfs.Observer = (*SimpleFS)(nil)
  2575  
  2576  // LocalChange implements the libkbfs.Observer interface for SimpleFS.
  2577  func (k *SimpleFS) LocalChange(
  2578  	ctx context.Context, node libkbfs.Node, _ libkbfs.WriteRange) {
  2579  	k.subscribeLock.RLock()
  2580  	defer k.subscribeLock.RUnlock()
  2581  	if node.GetFolderBranch() == k.subscribeCurrFB {
  2582  		k.config.Reporter().NotifyPathUpdated(ctx, k.subscribeCurrTlfPathFromGUI)
  2583  	}
  2584  }
  2585  
  2586  // BatchChanges implements the libkbfs.Observer interface for SimpleFS.
  2587  func (k *SimpleFS) BatchChanges(
  2588  	ctx context.Context, changes []libkbfs.NodeChange, _ []libkbfs.NodeID) {
  2589  	// Don't take any locks while processing these notifications,
  2590  	// since it risks deadlock.
  2591  	fbs := make(map[data.FolderBranch]bool, 1)
  2592  	for _, nc := range changes {
  2593  		fbs[nc.Node.GetFolderBranch()] = true
  2594  	}
  2595  
  2596  	go func() {
  2597  		k.subscribeLock.RLock()
  2598  		defer k.subscribeLock.RUnlock()
  2599  		if fbs[k.subscribeCurrFB] {
  2600  			k.config.Reporter().NotifyPathUpdated(ctx, k.subscribeCurrTlfPathFromGUI)
  2601  		}
  2602  	}()
  2603  }
  2604  
  2605  // TlfHandleChange implements the libkbfs.Observer interface for SimpleFS.
  2606  func (k *SimpleFS) TlfHandleChange(_ context.Context, _ *tlfhandle.Handle) {
  2607  	// TODO: the GUI might eventually care about a handle change.
  2608  }
  2609  
  2610  // SimpleFSGetUserQuotaUsage returns the quota usage information for
  2611  // the logged-in user.
  2612  func (k *SimpleFS) SimpleFSGetUserQuotaUsage(ctx context.Context) (
  2613  	res keybase1.SimpleFSQuotaUsage, err error) {
  2614  	ctx = k.makeContext(ctx)
  2615  	status, _, err := k.config.KBFSOps().Status(ctx)
  2616  	if err != nil {
  2617  		return keybase1.SimpleFSQuotaUsage{}, err
  2618  	}
  2619  	res.UsageBytes = status.UsageBytes
  2620  	res.ArchiveBytes = status.ArchiveBytes
  2621  	res.LimitBytes = status.LimitBytes
  2622  	res.GitUsageBytes = status.GitUsageBytes
  2623  	res.GitArchiveBytes = status.GitArchiveBytes
  2624  	res.GitLimitBytes = status.GitLimitBytes
  2625  	return res, nil
  2626  }
  2627  
  2628  // SimpleFSGetTeamQuotaUsage returns the quota usage information for
  2629  // the given team.
  2630  func (k *SimpleFS) SimpleFSGetTeamQuotaUsage(
  2631  	ctx context.Context, teamName keybase1.TeamName) (
  2632  	res keybase1.SimpleFSQuotaUsage, err error) {
  2633  	ctx = k.makeContext(ctx)
  2634  	path := keybase1.NewPathWithKbfsPath(
  2635  		fmt.Sprintf("team/%s", teamName.String()))
  2636  	fb, _, err := k.getFolderBranchFromPath(ctx, path)
  2637  	if err != nil {
  2638  		return keybase1.SimpleFSQuotaUsage{}, err
  2639  	}
  2640  	if fb == (data.FolderBranch{}) {
  2641  		return keybase1.SimpleFSQuotaUsage{}, nil
  2642  	}
  2643  
  2644  	status, _, err := k.config.KBFSOps().FolderStatus(ctx, fb)
  2645  	if err != nil {
  2646  		return keybase1.SimpleFSQuotaUsage{}, err
  2647  	}
  2648  
  2649  	res.UsageBytes = status.UsageBytes
  2650  	res.ArchiveBytes = status.ArchiveBytes
  2651  	res.LimitBytes = status.LimitBytes
  2652  	res.GitUsageBytes = status.GitUsageBytes
  2653  	res.GitArchiveBytes = status.GitArchiveBytes
  2654  	res.GitLimitBytes = status.GitLimitBytes
  2655  	return res, nil
  2656  }
  2657  
  2658  func (k *SimpleFS) getSyncConfig(ctx context.Context, path keybase1.Path) (
  2659  	tlfID tlf.ID, config keybase1.FolderSyncConfig,
  2660  	err error) {
  2661  	t, tlfName, _, _, err := remoteTlfAndPath(path)
  2662  	if err != nil {
  2663  		return tlf.NullID, keybase1.FolderSyncConfig{}, err
  2664  	}
  2665  	kbpki, err := k.getKBPKI(ctx)
  2666  	if err != nil {
  2667  		return tlf.NullID, keybase1.FolderSyncConfig{}, err
  2668  	}
  2669  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  2670  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  2671  	if err != nil {
  2672  		return tlf.NullID, keybase1.FolderSyncConfig{}, err
  2673  	}
  2674  
  2675  	// Ensure the TLF is initialized by getting the root node first.
  2676  	_, _, err = k.config.KBFSOps().GetRootNode(
  2677  		ctx, tlfHandle, data.MasterBranch)
  2678  	if err != nil {
  2679  		return tlf.NullID, keybase1.FolderSyncConfig{}, err
  2680  	}
  2681  
  2682  	config, err = k.config.KBFSOps().GetSyncConfig(ctx, tlfHandle.TlfID())
  2683  	if err != nil {
  2684  		return tlf.NullID, keybase1.FolderSyncConfig{}, err
  2685  	}
  2686  	return tlfHandle.TlfID(), config, nil
  2687  }
  2688  
  2689  func (k *SimpleFS) filterEmptyErr(
  2690  	ctx context.Context, path string, err error) error {
  2691  	exitEarly, _ := libfs.FilterTLFEarlyExitError(
  2692  		ctx, err, k.log, tlf.CanonicalName(path) /* just for logging */)
  2693  	if exitEarly {
  2694  		return nil
  2695  	}
  2696  	return err
  2697  }
  2698  
  2699  // SimpleFSFolderSyncConfigAndStatus gets the given folder's sync config.
  2700  func (k *SimpleFS) SimpleFSFolderSyncConfigAndStatus(
  2701  	ctx context.Context, path keybase1.Path) (
  2702  	_ keybase1.FolderSyncConfigAndStatus, err error) {
  2703  	defer func() {
  2704  		err = k.filterEmptyErr(ctx, path.String(), err)
  2705  	}()
  2706  	ctx = k.makeContext(ctx)
  2707  	ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil)
  2708  	if err != nil {
  2709  		return keybase1.FolderSyncConfigAndStatus{}, err
  2710  	}
  2711  	_, config, err := k.getSyncConfig(ctx, path)
  2712  	if err != nil {
  2713  		return keybase1.FolderSyncConfigAndStatus{}, err
  2714  	}
  2715  	res := keybase1.FolderSyncConfigAndStatus{Config: config}
  2716  
  2717  	dbc := k.config.DiskBlockCache()
  2718  	if config.Mode != keybase1.FolderSyncMode_DISABLED {
  2719  		fs, finalElem, err := k.getFSIfExists(ctx, path)
  2720  		if err != nil {
  2721  			return res, err
  2722  		}
  2723  		// Use LStat so we don't follow symlinks.
  2724  		fi, err := fs.Lstat(finalElem)
  2725  		if err != nil {
  2726  			return res, err
  2727  		}
  2728  
  2729  		if kmg, ok := fi.Sys().(libfs.KBFSMetadataForSimpleFSGetter); ok {
  2730  			metadata, err := kmg.KBFSMetadataForSimpleFS()
  2731  			if err != nil {
  2732  				return keybase1.FolderSyncConfigAndStatus{}, err
  2733  			}
  2734  			res.Status.PrefetchStatus = metadata.PrefetchStatus
  2735  			res.Status.PrefetchProgress =
  2736  				metadata.PrefetchProgress.ToProtocolProgress(k.config.Clock())
  2737  
  2738  			libfs, ok := fs.(*libfs.FS)
  2739  			if dbc != nil && ok {
  2740  				size, err := dbc.GetTlfSize(
  2741  					ctx, libfs.RootNode().GetFolderBranch().Tlf,
  2742  					libkbfs.DiskBlockSyncCache)
  2743  				if err != nil {
  2744  					return res, err
  2745  				}
  2746  				res.Status.StoredBytesTotal = int64(size)
  2747  			}
  2748  		} else {
  2749  			k.log.CDebugf(ctx,
  2750  				"Could not get prefetch status from filesys: %T", fi.Sys())
  2751  		}
  2752  	}
  2753  
  2754  	libkbfs.FillInDiskSpaceStatus(
  2755  		ctx, &res.Status, res.Status.PrefetchStatus, dbc)
  2756  	return res, err
  2757  }
  2758  
  2759  // SimpleFSSetFolderSyncConfig implements the SimpleFSInterface.
  2760  func (k *SimpleFS) SimpleFSSetFolderSyncConfig(
  2761  	ctx context.Context, arg keybase1.SimpleFSSetFolderSyncConfigArg) (err error) {
  2762  	ctx = k.makeContext(ctx)
  2763  	ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &arg.Path, nil)
  2764  	if err != nil {
  2765  		return err
  2766  	}
  2767  	tlfID, _, err := k.getSyncConfig(ctx, arg.Path)
  2768  	if err != nil {
  2769  		return err
  2770  	}
  2771  
  2772  	_, err = k.config.KBFSOps().SetSyncConfig(ctx, tlfID, arg.Config)
  2773  	return err
  2774  }
  2775  
  2776  // SimpleFSGetFolder implements the SimpleFSInterface.
  2777  func (k *SimpleFS) SimpleFSGetFolder(
  2778  	ctx context.Context, kbfsPath keybase1.KBFSPath) (
  2779  	res keybase1.FolderWithFavFlags, err error) {
  2780  	defer func() { err = translateErr(err) }()
  2781  	ctx, err = k.makeContextWithIdentifyBehavior(ctx, kbfsPath.IdentifyBehavior)
  2782  	if err != nil {
  2783  		return keybase1.FolderWithFavFlags{}, err
  2784  	}
  2785  	t, tlfName, _, _, err := remoteTlfAndPath(keybase1.NewPathWithKbfs(kbfsPath))
  2786  	if err != nil {
  2787  		return keybase1.FolderWithFavFlags{}, err
  2788  	}
  2789  	kbpki, err := k.getKBPKI(ctx)
  2790  	if err != nil {
  2791  		return keybase1.FolderWithFavFlags{}, err
  2792  	}
  2793  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  2794  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  2795  	if err != nil {
  2796  		return keybase1.FolderWithFavFlags{}, err
  2797  	}
  2798  	return k.config.KBFSOps().GetFolderWithFavFlags(ctx, tlfHandle)
  2799  }
  2800  
  2801  func (k *SimpleFS) getFolder(
  2802  	ctx context.Context, tlfID tlf.ID, md libkbfs.SyncedTlfMD,
  2803  	session idutil.SessionInfo) (keybase1.Folder, error) {
  2804  	return keybase1.Folder{
  2805  		Name:       string(md.Handle.GetPreferredFormat(session.Name)),
  2806  		FolderType: tlfID.Type().FolderType(),
  2807  		Private:    tlfID.Type() != tlf.Public,
  2808  	}, nil
  2809  }
  2810  
  2811  // SimpleFSSyncConfigAndStatus implements the SimpleFSInterface.
  2812  func (k *SimpleFS) SimpleFSSyncConfigAndStatus(ctx context.Context,
  2813  	identifyBehavior *keybase1.TLFIdentifyBehavior) (
  2814  	res keybase1.SyncConfigAndStatusRes, err error) {
  2815  	ctx, err = k.makeContextWithIdentifyBehavior(ctx, identifyBehavior)
  2816  	if err != nil {
  2817  		return keybase1.SyncConfigAndStatusRes{}, err
  2818  	}
  2819  	dbc := k.config.DiskBlockCache()
  2820  	bytesAvail, bytesTotal := libkbfs.GetLocalDiskStats(ctx, dbc)
  2821  
  2822  	hasRoom := true
  2823  	if dbc != nil {
  2824  		hasRoom, _, err = dbc.DoesCacheHaveSpace(ctx, libkbfs.DiskBlockSyncCache)
  2825  		if err != nil {
  2826  			return keybase1.SyncConfigAndStatusRes{}, err
  2827  		}
  2828  	}
  2829  
  2830  	tlfMDs := k.config.KBFSOps().GetAllSyncedTlfMDs(ctx)
  2831  	kbpki, err := k.getKBPKI(ctx)
  2832  	if err != nil {
  2833  		return keybase1.SyncConfigAndStatusRes{}, err
  2834  	}
  2835  	session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true)
  2836  	if err != nil {
  2837  		return keybase1.SyncConfigAndStatusRes{}, err
  2838  	}
  2839  
  2840  	res.Folders = make(
  2841  		[]keybase1.FolderSyncConfigAndStatusWithFolder, len(tlfMDs))
  2842  	allNotStarted := true
  2843  	i := 0
  2844  	for tlfID, md := range tlfMDs {
  2845  		config, err := k.config.KBFSOps().GetSyncConfig(ctx, tlfID)
  2846  		if err != nil {
  2847  			return keybase1.SyncConfigAndStatusRes{}, err
  2848  		}
  2849  
  2850  		if config.Mode == keybase1.FolderSyncMode_DISABLED {
  2851  			return keybase1.SyncConfigAndStatusRes{}, errors.Errorf(
  2852  				"Folder %s has sync unexpectedly disabled", tlfID)
  2853  		}
  2854  
  2855  		f, err := k.getFolder(ctx, tlfID, md, session)
  2856  		if err != nil {
  2857  			return keybase1.SyncConfigAndStatusRes{}, err
  2858  		}
  2859  
  2860  		res.Folders[i].Folder = f
  2861  		res.Folders[i].Config = config
  2862  		status := md.MD.PrefetchStatus.ToProtocolStatus()
  2863  		res.Folders[i].Status.PrefetchStatus = status
  2864  		if status != keybase1.PrefetchStatus_NOT_STARTED {
  2865  			allNotStarted = false
  2866  		}
  2867  		if md.MD.PrefetchProgress != nil {
  2868  			res.Folders[i].Status.PrefetchProgress =
  2869  				md.MD.PrefetchProgress.ToProtocolProgress(k.config.Clock())
  2870  		}
  2871  		res.Folders[i].Status.LocalDiskBytesAvailable = bytesAvail
  2872  		res.Folders[i].Status.LocalDiskBytesTotal = bytesTotal
  2873  		if res.Folders[i].Status.PrefetchStatus !=
  2874  			keybase1.PrefetchStatus_COMPLETE {
  2875  			res.Folders[i].Status.OutOfSyncSpace = !hasRoom
  2876  		}
  2877  
  2878  		if dbc != nil {
  2879  			size, err := dbc.GetTlfSize(ctx, tlfID, libkbfs.DiskBlockSyncCache)
  2880  			if err != nil {
  2881  				return keybase1.SyncConfigAndStatusRes{}, err
  2882  			}
  2883  			res.Folders[i].Status.StoredBytesTotal = int64(size)
  2884  		}
  2885  
  2886  		i++
  2887  	}
  2888  
  2889  	// Sort by folder name.
  2890  	sort.SliceStable(res.Folders, func(i, j int) bool {
  2891  		return res.Folders[i].Folder.ToString() <
  2892  			res.Folders[j].Folder.ToString()
  2893  	})
  2894  
  2895  	if len(tlfMDs) > 0 {
  2896  		p := k.config.BlockOps().Prefetcher().OverallSyncStatus()
  2897  		res.OverallStatus.PrefetchProgress = p.ToProtocolProgress(
  2898  			k.config.Clock())
  2899  		if allNotStarted {
  2900  			res.OverallStatus.PrefetchStatus =
  2901  				keybase1.PrefetchStatus_NOT_STARTED
  2902  		} else {
  2903  			res.OverallStatus.PrefetchStatus = p.ToProtocolStatus()
  2904  		}
  2905  	}
  2906  
  2907  	res.OverallStatus.LocalDiskBytesAvailable = bytesAvail
  2908  	res.OverallStatus.LocalDiskBytesTotal = bytesTotal
  2909  	if res.OverallStatus.PrefetchStatus !=
  2910  		keybase1.PrefetchStatus_COMPLETE {
  2911  		res.OverallStatus.OutOfSyncSpace = !hasRoom
  2912  	}
  2913  
  2914  	if dbc != nil {
  2915  		statusMap := dbc.Status(ctx)
  2916  		status, ok := statusMap["SyncBlockCache"]
  2917  		if ok {
  2918  			res.OverallStatus.StoredBytesTotal = int64(status.BlockBytes)
  2919  		}
  2920  	}
  2921  
  2922  	return res, nil
  2923  }
  2924  
  2925  // SimpleFSClearConflictState implements the SimpleFS interface.
  2926  func (k *SimpleFS) SimpleFSClearConflictState(ctx context.Context,
  2927  	path keybase1.Path) (err error) {
  2928  	ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil)
  2929  	if err != nil {
  2930  		return err
  2931  	}
  2932  	ctx, err = k.startOpWrapContext(k.makeContext(ctx))
  2933  	if err != nil {
  2934  		return err
  2935  	}
  2936  	defer func() {
  2937  		err := libcontext.CleanupCancellationDelayer(ctx)
  2938  		if err != nil {
  2939  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  2940  		}
  2941  	}()
  2942  	t, tlfName, _, _, err := remoteTlfAndPath(path)
  2943  	if err != nil {
  2944  		return err
  2945  	}
  2946  	kbpki, err := k.getKBPKI(ctx)
  2947  	if err != nil {
  2948  		return err
  2949  	}
  2950  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  2951  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  2952  	if err != nil {
  2953  		return err
  2954  	}
  2955  	tlfID := tlfHandle.TlfID()
  2956  	return k.config.KBFSOps().ClearConflictView(ctx, tlfID)
  2957  }
  2958  
  2959  // SimpleFSFinishResolvingConflict implements the SimpleFS interface.
  2960  func (k *SimpleFS) SimpleFSFinishResolvingConflict(ctx context.Context,
  2961  	path keybase1.Path) (err error) {
  2962  	ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil)
  2963  	if err != nil {
  2964  		return err
  2965  	}
  2966  	ctx, err = k.startOpWrapContext(k.makeContext(ctx))
  2967  	if err != nil {
  2968  		return err
  2969  	}
  2970  	defer func() {
  2971  		err := libcontext.CleanupCancellationDelayer(ctx)
  2972  		if err != nil {
  2973  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  2974  		}
  2975  	}()
  2976  	t, tlfName, _, _, err := remoteTlfAndPath(path)
  2977  	if err != nil {
  2978  		return err
  2979  	}
  2980  	kbpki, err := k.getKBPKI(ctx)
  2981  	if err != nil {
  2982  		return err
  2983  	}
  2984  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  2985  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  2986  	if err != nil {
  2987  		return err
  2988  	}
  2989  	tlfID := tlfHandle.TlfID()
  2990  	branch, err := k.branchNameFromPath(ctx, tlfHandle, path)
  2991  	if err != nil {
  2992  		return err
  2993  	}
  2994  	return k.config.KBFSOps().FinishResolvingConflict(ctx, data.FolderBranch{
  2995  		Tlf:    tlfID,
  2996  		Branch: branch,
  2997  	})
  2998  }
  2999  
  3000  // SimpleFSForceStuckConflict implements the SimpleFS interface.
  3001  func (k *SimpleFS) SimpleFSForceStuckConflict(
  3002  	ctx context.Context, path keybase1.Path) (err error) {
  3003  	ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil)
  3004  	if err != nil {
  3005  		return err
  3006  	}
  3007  	ctx, err = k.startOpWrapContext(k.makeContext(ctx))
  3008  	if err != nil {
  3009  		return err
  3010  	}
  3011  	defer func() {
  3012  		err := libcontext.CleanupCancellationDelayer(ctx)
  3013  		if err != nil {
  3014  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  3015  		}
  3016  	}()
  3017  	t, tlfName, _, _, err := remoteTlfAndPath(path)
  3018  	if err != nil {
  3019  		return err
  3020  	}
  3021  	kbpki, err := k.getKBPKI(ctx)
  3022  	if err != nil {
  3023  		return err
  3024  	}
  3025  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  3026  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  3027  	if err != nil {
  3028  		return err
  3029  	}
  3030  	tlfID := tlfHandle.TlfID()
  3031  	return k.config.KBFSOps().ForceStuckConflictForTesting(ctx, tlfID)
  3032  }
  3033  
  3034  // SimpleFSGetOnlineStatus implements the SimpleFSInterface.
  3035  func (k *SimpleFS) SimpleFSGetOnlineStatus(
  3036  	ctx context.Context, clientID string) (keybase1.KbfsOnlineStatus, error) {
  3037  	return k.subscriptionManager(clientID).OnlineStatusTracker().GetOnlineStatus(), nil
  3038  }
  3039  
  3040  // SimpleFSUserIn implements the SimpleFSInterface.
  3041  func (k *SimpleFS) SimpleFSUserIn(ctx context.Context, clientID string) error {
  3042  	k.subscriptionManager(clientID).OnlineStatusTracker().UserIn(ctx, clientID)
  3043  	return nil
  3044  }
  3045  
  3046  // SimpleFSUserOut implements the SimpleFSInterface.
  3047  func (k *SimpleFS) SimpleFSUserOut(ctx context.Context, clientID string) error {
  3048  	k.subscriptionManager(clientID).OnlineStatusTracker().UserOut(ctx, clientID)
  3049  	return nil
  3050  }
  3051  
  3052  // SimpleFSCheckReachability implements the SimpleFSInterface.
  3053  func (k *SimpleFS) SimpleFSCheckReachability(ctx context.Context) error {
  3054  	ctx = k.makeContext(ctx)
  3055  	mdServer := k.config.MDServer()
  3056  	if mdServer != nil {
  3057  		// KeybaseService (which holds SimpleFS service) gets init'ed before
  3058  		// MDServer is set. HOTPOT-1269
  3059  		mdServer.CheckReachability(ctx)
  3060  	}
  3061  	return nil
  3062  }
  3063  
  3064  // SimpleFSSetDebugLevel implements the SimpleFSInterface.
  3065  func (k *SimpleFS) SimpleFSSetDebugLevel(
  3066  	_ context.Context, level string) error {
  3067  	k.config.SetVLogLevel(level)
  3068  	return nil
  3069  }
  3070  
  3071  // SimpleFSSettings implements the SimpleFSInterface.
  3072  func (k *SimpleFS) SimpleFSSettings(ctx context.Context) (settings keybase1.FSSettings, err error) {
  3073  	defer func() {
  3074  		k.log.CDebugf(ctx, "SimpleFSSettings settings=%+v err=%+v", settings, err)
  3075  	}()
  3076  	db := k.config.GetSettingsDB()
  3077  	if db == nil {
  3078  		return keybase1.FSSettings{}, libkbfs.ErrNoSettingsDB
  3079  	}
  3080  	return db.Settings(ctx)
  3081  }
  3082  
  3083  // SimpleFSSetNotificationThreshold implements the SimpleFSInterface.
  3084  func (k *SimpleFS) SimpleFSSetNotificationThreshold(ctx context.Context, threshold int64) (err error) {
  3085  	defer func() {
  3086  		k.log.CDebugf(ctx, "SimpleFSSetNotificationThreshold threshold=%d err=%+v", threshold, err)
  3087  	}()
  3088  	db := k.config.GetSettingsDB()
  3089  	if db == nil {
  3090  		return libkbfs.ErrNoSettingsDB
  3091  	}
  3092  	if err = db.SetNotificationThreshold(ctx, threshold); err != nil {
  3093  		return err
  3094  	}
  3095  	k.config.SubscriptionManagerPublisher().PublishChange(keybase1.SubscriptionTopic_SETTINGS)
  3096  	return nil
  3097  }
  3098  
  3099  // SimpleFSObfuscatePath implements the SimpleFSInterface.
  3100  func (k *SimpleFS) SimpleFSObfuscatePath(
  3101  	ctx context.Context, path keybase1.Path) (res string, err error) {
  3102  	ctx, err = k.startOpWrapContext(k.makeContext(ctx))
  3103  	if err != nil {
  3104  		return "", err
  3105  	}
  3106  	defer func() {
  3107  		err := libcontext.CleanupCancellationDelayer(ctx)
  3108  		if err != nil {
  3109  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  3110  		}
  3111  	}()
  3112  	t, tlfName, midPath, finalElem, err := remoteTlfAndPath(path)
  3113  	if err != nil {
  3114  		return "", err
  3115  	}
  3116  	kbpki, err := k.getKBPKI(ctx)
  3117  	if err != nil {
  3118  		return "", err
  3119  	}
  3120  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  3121  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  3122  	if err != nil {
  3123  		return "", err
  3124  	}
  3125  	branch, err := k.branchNameFromPath(ctx, tlfHandle, path)
  3126  	if err != nil {
  3127  		return "", err
  3128  	}
  3129  	fs, err := k.newFS(
  3130  		ctx, k.config, tlfHandle, branch, "", false)
  3131  	if err != nil {
  3132  		return "", err
  3133  	}
  3134  	asLibFS, ok := fs.(*libfs.FS)
  3135  	if !ok {
  3136  		return "", errors.Errorf("FS was not a KBFS file system: %T", fs)
  3137  	}
  3138  	p := fs.Join(midPath, finalElem)
  3139  	return stdpath.Join(
  3140  		tlfHandle.GetCanonicalPath(), asLibFS.PathForLogging(p)), nil
  3141  }
  3142  
  3143  // SimpleFSDeobfuscatePath implements the SimpleFSInterface.
  3144  func (k *SimpleFS) SimpleFSDeobfuscatePath(
  3145  	ctx context.Context, path keybase1.Path) (res []string, err error) {
  3146  	ctx, err = k.startOpWrapContext(k.makeContext(ctx))
  3147  	if err != nil {
  3148  		return nil, err
  3149  	}
  3150  	defer func() {
  3151  		err := libcontext.CleanupCancellationDelayer(ctx)
  3152  		if err != nil {
  3153  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  3154  		}
  3155  	}()
  3156  	t, tlfName, midPath, finalElem, err := remoteTlfAndPath(path)
  3157  	if err != nil {
  3158  		return nil, err
  3159  	}
  3160  	kbpki, err := k.getKBPKI(ctx)
  3161  	if err != nil {
  3162  		return nil, err
  3163  	}
  3164  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  3165  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  3166  	if err != nil {
  3167  		return nil, err
  3168  	}
  3169  	branch, err := k.branchNameFromPath(ctx, tlfHandle, path)
  3170  	if err != nil {
  3171  		return nil, err
  3172  	}
  3173  	fs, err := k.newFS(
  3174  		ctx, k.config, tlfHandle, branch, "", false)
  3175  	if err != nil {
  3176  		return nil, err
  3177  	}
  3178  	asLibFS, ok := fs.(*libfs.FS)
  3179  	if !ok {
  3180  		return nil, errors.Errorf("FS was not a KBFS file system: %T", fs)
  3181  	}
  3182  	p := fs.Join(midPath, finalElem)
  3183  	resWithoutPrefix, err := libfs.Deobfuscate(ctx, asLibFS, p)
  3184  	if err != nil {
  3185  		return nil, err
  3186  	}
  3187  	for _, r := range resWithoutPrefix {
  3188  		res = append(res, stdpath.Join(tlfHandle.GetCanonicalPath(), r))
  3189  	}
  3190  	if len(res) == 0 {
  3191  		return nil, errors.New("Found no matching paths")
  3192  	}
  3193  	return res, nil
  3194  }
  3195  
  3196  // SimpleFSGetStats implements the SimpleFSInterface.
  3197  func (k *SimpleFS) SimpleFSGetStats(ctx context.Context) (
  3198  	res keybase1.SimpleFSStats, err error) {
  3199  	ctx = k.makeContext(ctx)
  3200  	dbc := k.config.DiskBlockCache()
  3201  	if dbc == nil {
  3202  		return keybase1.SimpleFSStats{}, nil
  3203  	}
  3204  
  3205  	res.ProcessStats = runtimestats.GetProcessStats(keybase1.ProcessType_KBFS)
  3206  
  3207  	statusMap := dbc.Status(ctx)
  3208  	if status, ok := statusMap["SyncBlockCache"]; ok {
  3209  		res.SyncCacheDbStats = status.BlockDBStats
  3210  
  3211  		res.RuntimeDbStats = append(res.RuntimeDbStats,
  3212  			keybase1.DbStats{
  3213  				Type:            keybase1.DbType_FS_SYNC_BLOCK_CACHE,
  3214  				MemCompActive:   status.MemCompActive,
  3215  				TableCompActive: status.TableCompActive,
  3216  			})
  3217  		res.RuntimeDbStats = append(res.RuntimeDbStats,
  3218  			keybase1.DbStats{
  3219  				Type:            keybase1.DbType_FS_SYNC_BLOCK_CACHE_META,
  3220  				MemCompActive:   status.MetaMemCompActive,
  3221  				TableCompActive: status.MetaTableCompActive,
  3222  			})
  3223  	}
  3224  	if status, ok := statusMap["WorkingSetBlockCache"]; ok {
  3225  		res.BlockCacheDbStats = status.BlockDBStats
  3226  		res.RuntimeDbStats = append(res.RuntimeDbStats,
  3227  			keybase1.DbStats{
  3228  				Type:            keybase1.DbType_FS_BLOCK_CACHE,
  3229  				MemCompActive:   status.MemCompActive,
  3230  				TableCompActive: status.TableCompActive,
  3231  			})
  3232  		res.RuntimeDbStats = append(res.RuntimeDbStats,
  3233  			keybase1.DbStats{
  3234  				Type:            keybase1.DbType_FS_BLOCK_CACHE_META,
  3235  				MemCompActive:   status.MetaMemCompActive,
  3236  				TableCompActive: status.MetaTableCompActive,
  3237  			})
  3238  	}
  3239  	return res, nil
  3240  }
  3241  
  3242  func (k *SimpleFS) subscriptionManager(
  3243  	clientID string) libkbfs.SubscriptionManager {
  3244  	return k.config.SubscriptionManager(
  3245  		libkbfs.SubscriptionManagerClientID(clientID), true,
  3246  		k.subscriptionNotifier)
  3247  }
  3248  
  3249  // SimpleFSSubscribePath implements the SimpleFSInterface.
  3250  func (k *SimpleFS) SimpleFSSubscribePath(
  3251  	ctx context.Context, arg keybase1.SimpleFSSubscribePathArg) (err error) {
  3252  	defer func() {
  3253  		err = k.filterEmptyErr(ctx, arg.KbfsPath, err)
  3254  		err = translateErr(err)
  3255  	}()
  3256  	ctx, err = k.makeContextWithIdentifyBehavior(ctx, arg.IdentifyBehavior)
  3257  	if err != nil {
  3258  		return err
  3259  	}
  3260  	interval := time.Second * time.Duration(arg.DeduplicateIntervalSecond)
  3261  	return k.subscriptionManager(arg.ClientID).SubscribePath(
  3262  		ctx, libkbfs.SubscriptionID(arg.SubscriptionID),
  3263  		arg.KbfsPath, arg.Topic, &interval)
  3264  }
  3265  
  3266  // SimpleFSSubscribeNonPath implements the SimpleFSInterface.
  3267  func (k *SimpleFS) SimpleFSSubscribeNonPath(
  3268  	ctx context.Context, arg keybase1.SimpleFSSubscribeNonPathArg) (err error) {
  3269  	ctx, err = k.makeContextWithIdentifyBehavior(ctx, arg.IdentifyBehavior)
  3270  	if err != nil {
  3271  		return err
  3272  	}
  3273  	interval := time.Second * time.Duration(arg.DeduplicateIntervalSecond)
  3274  	return k.subscriptionManager(arg.ClientID).SubscribeNonPath(
  3275  		ctx, libkbfs.SubscriptionID(arg.SubscriptionID), arg.Topic, &interval)
  3276  }
  3277  
  3278  // SimpleFSUnsubscribe implements the SimpleFSInterface.
  3279  func (k *SimpleFS) SimpleFSUnsubscribe(
  3280  	ctx context.Context, arg keybase1.SimpleFSUnsubscribeArg) (err error) {
  3281  	ctx, err = k.makeContextWithIdentifyBehavior(ctx, arg.IdentifyBehavior)
  3282  	if err != nil {
  3283  		return err
  3284  	}
  3285  	k.subscriptionManager(arg.ClientID).Unsubscribe(
  3286  		ctx, libkbfs.SubscriptionID(arg.SubscriptionID))
  3287  	return nil
  3288  }
  3289  
  3290  // SimpleFSStartDownload implements the SimpleFSInterface.
  3291  func (k *SimpleFS) SimpleFSStartDownload(
  3292  	ctx context.Context, arg keybase1.SimpleFSStartDownloadArg) (
  3293  	downloadID string, err error) {
  3294  	return k.getDownloadManager().startDownload(ctx, arg)
  3295  }
  3296  
  3297  // SimpleFSGetDownloadStatus implements the SimpleFSInterface.
  3298  func (k *SimpleFS) SimpleFSGetDownloadStatus(ctx context.Context) (
  3299  	status keybase1.DownloadStatus, err error) {
  3300  	return k.getDownloadManager().getDownloadStatus(ctx), nil
  3301  }
  3302  
  3303  // SimpleFSCancelDownload implements the SimpleFSInterface.
  3304  func (k *SimpleFS) SimpleFSCancelDownload(
  3305  	ctx context.Context, downloadID string) (err error) {
  3306  	return k.getDownloadManager().cancelDownload(ctx, downloadID)
  3307  }
  3308  
  3309  // SimpleFSDismissDownload implements the SimpleFSInterface.
  3310  func (k *SimpleFS) SimpleFSDismissDownload(
  3311  	ctx context.Context, downloadID string) (err error) {
  3312  	k.getDownloadManager().dismissDownload(ctx, downloadID)
  3313  	return nil
  3314  }
  3315  
  3316  // SimpleFSGetDownloadInfo implements the SimpleFSInterface.
  3317  func (k *SimpleFS) SimpleFSGetDownloadInfo(
  3318  	ctx context.Context, downloadID string) (
  3319  	downloadInfo keybase1.DownloadInfo, err error) {
  3320  	return k.getDownloadManager().getDownloadInfo(downloadID)
  3321  }
  3322  
  3323  // SimpleFSConfigureDownload implements the SimpleFSInterface.
  3324  func (k *SimpleFS) SimpleFSConfigureDownload(
  3325  	ctx context.Context, arg keybase1.SimpleFSConfigureDownloadArg) (err error) {
  3326  	k.getDownloadManager().configureDownload(arg.CacheDirOverride, arg.DownloadDirOverride)
  3327  	return nil
  3328  }
  3329  
  3330  // Copied from net/http/sniff.go: the algorithm uses at most sniffLen bytes to
  3331  // make its decision.
  3332  const sniffLen = 512
  3333  
  3334  // getContentType detects the content type of the file located at kbfsPath.
  3335  // It's adapted from serveContent in net/http/fs.go. The algorithm might change
  3336  // in the future, but it's OK as we are using the invariance mechanism and the
  3337  // check in libhttpserver happens right before writing the content, using the
  3338  // real HTTP headers from the http package.
  3339  func (k *SimpleFS) getContentType(ctx context.Context, kbfsPath keybase1.KBFSPath) (
  3340  	contentType string, err error) {
  3341  	contentType = mime.TypeByExtension(filepath.Ext(kbfsPath.Path))
  3342  	if len(contentType) > 0 {
  3343  		return contentType, nil
  3344  	}
  3345  
  3346  	fs, finalElem, err := k.getFS(ctx, keybase1.NewPathWithKbfs(kbfsPath))
  3347  	if err != nil {
  3348  		return "", err
  3349  	}
  3350  	f, err := fs.OpenFile(finalElem, os.O_RDONLY, 0644)
  3351  	if err != nil {
  3352  		return "", err
  3353  	}
  3354  	var buf [sniffLen]byte
  3355  	n, _ := io.ReadFull(f, buf[:])
  3356  	return http.DetectContentType(buf[:n]), nil
  3357  }
  3358  
  3359  // SimpleFSGetGUIFileContext implements the SimpleFSInterface.
  3360  func (k *SimpleFS) SimpleFSGetGUIFileContext(ctx context.Context,
  3361  	kbfsPath keybase1.KBFSPath) (resource keybase1.GUIFileContext, err error) {
  3362  	wrappedPath := keybase1.NewPathWithKbfs(kbfsPath)
  3363  	ctx, err = k.startSyncOp(ctx, "GetGUIFileContext", "", &wrappedPath, nil)
  3364  	if err != nil {
  3365  		return keybase1.GUIFileContext{}, err
  3366  	}
  3367  	defer func() { k.doneSyncOp(ctx, err) }()
  3368  
  3369  	if len(kbfsPath.Path) == 0 {
  3370  		return keybase1.GUIFileContext{}, errors.New("empty path")
  3371  	}
  3372  	if k.localHTTPServer == nil {
  3373  		return keybase1.GUIFileContext{}, errors.New("HTTP server is disabled")
  3374  	}
  3375  
  3376  	contentType, err := k.getContentType(ctx, kbfsPath)
  3377  	if err != nil {
  3378  		return keybase1.GUIFileContext{}, err
  3379  	}
  3380  	viewType, invariance := libhttpserver.GetGUIFileContextFromContentType(contentType)
  3381  
  3382  	token, err := k.localHTTPServer.CurrentToken()
  3383  	if err != nil {
  3384  		return keybase1.GUIFileContext{}, err
  3385  	}
  3386  	address, err := k.localHTTPServer.Address()
  3387  	if err != nil {
  3388  		return keybase1.GUIFileContext{}, err
  3389  	}
  3390  
  3391  	u := url.URL{
  3392  		Scheme:   "http",
  3393  		Host:     address,
  3394  		Path:     path.Join("/files", kbfsPath.Path),
  3395  		RawQuery: "token=" + token + "&viewTypeInvariance=" + invariance,
  3396  	}
  3397  
  3398  	return keybase1.GUIFileContext{
  3399  		ContentType: contentType,
  3400  		ViewType:    viewType,
  3401  		Url:         u.String(),
  3402  	}, nil
  3403  }
  3404  
  3405  const kbfsOpsWaitDuration = 200 * time.Millisecond
  3406  const kbfsOpsWaitTimeout = 4 * time.Second
  3407  
  3408  func (k *SimpleFS) waitForKBFSOps(ctx context.Context) error {
  3409  	for {
  3410  		if k.config.KBFSOps() != nil {
  3411  			return nil
  3412  		}
  3413  		select {
  3414  		case <-ctx.Done():
  3415  			return ctx.Err()
  3416  		default:
  3417  			time.Sleep(kbfsOpsWaitDuration)
  3418  		}
  3419  	}
  3420  }
  3421  
  3422  // SimpleFSGetFilesTabBadge implements the SimpleFSInterface.
  3423  func (k *SimpleFS) SimpleFSGetFilesTabBadge(ctx context.Context) (
  3424  	keybase1.FilesTabBadge, error) {
  3425  	ctx, cancel := context.WithTimeout(ctx, kbfsOpsWaitTimeout)
  3426  	defer cancel()
  3427  	if err := k.waitForKBFSOps(ctx); err != nil {
  3428  		return 0, err
  3429  	}
  3430  	return k.config.KBFSOps().GetBadge(ctx)
  3431  }
  3432  
  3433  // SimpleFSSetSfmiBannerDismissed implements the SimpleFSInterface.
  3434  func (k *SimpleFS) SimpleFSSetSfmiBannerDismissed(
  3435  	ctx context.Context, dismissed bool) (err error) {
  3436  	defer func() {
  3437  		k.log.CDebugf(ctx, "SimpleFSSetSfmiBannerDismissed err=%+v", err)
  3438  	}()
  3439  	db := k.config.GetSettingsDB()
  3440  	if db == nil {
  3441  		return libkbfs.ErrNoSettingsDB
  3442  	}
  3443  	if err = db.SetSfmiBannerDismissed(ctx, dismissed); err != nil {
  3444  		return err
  3445  	}
  3446  	k.config.SubscriptionManagerPublisher().PublishChange(keybase1.SubscriptionTopic_SETTINGS)
  3447  	return nil
  3448  }
  3449  
  3450  // SimpleFSSetSyncOnCellular implements the SimpleFSInterface.
  3451  func (k *SimpleFS) SimpleFSSetSyncOnCellular(
  3452  	ctx context.Context, syncOnCellular bool) (err error) {
  3453  	defer func() {
  3454  		k.log.CDebugf(ctx, "SimpleFSSetSyncOnCellular err=%+v", err)
  3455  	}()
  3456  	db := k.config.GetSettingsDB()
  3457  	if db == nil {
  3458  		return libkbfs.ErrNoSettingsDB
  3459  	}
  3460  	if err = db.SetSyncOnCellular(ctx, syncOnCellular); err != nil {
  3461  		return err
  3462  	}
  3463  	k.config.SubscriptionManagerPublisher().PublishChange(
  3464  		keybase1.SubscriptionTopic_SETTINGS)
  3465  	return nil
  3466  }
  3467  
  3468  // SimpleFSSearch implements the SimpleFSInterface.
  3469  func (k *SimpleFS) SimpleFSSearch(
  3470  	ctx context.Context, arg keybase1.SimpleFSSearchArg) (
  3471  	res keybase1.SimpleFSSearchResults, err error) {
  3472  	if k.getIndexer() == nil {
  3473  		return keybase1.SimpleFSSearchResults{},
  3474  			errors.New("Indexing not enabled")
  3475  	}
  3476  
  3477  	results, nextResult, err := k.getIndexer().Search(
  3478  		ctx, arg.Query, arg.NumResults, arg.StartingFrom)
  3479  	if err != nil {
  3480  		return keybase1.SimpleFSSearchResults{}, err
  3481  	}
  3482  
  3483  	res.Hits = make([]keybase1.SimpleFSSearchHit, len(results))
  3484  	for i, result := range results {
  3485  		res.Hits[i].Path = result.Path
  3486  	}
  3487  	res.NextResult = nextResult
  3488  	return res, nil
  3489  }
  3490  
  3491  // SimpleFSResetIndex implements the SimpleFSInterface.
  3492  func (k *SimpleFS) SimpleFSResetIndex(ctx context.Context) error {
  3493  	if k.getIndexer() == nil {
  3494  		return errors.New("Indexing not enabled")
  3495  	}
  3496  
  3497  	return k.getIndexer().ResetIndex(ctx)
  3498  }
  3499  
  3500  // SimpleFSGetIndexProgress implements the SimpleFSInterface.
  3501  func (k *SimpleFS) SimpleFSGetIndexProgress(
  3502  	ctx context.Context) (res keybase1.SimpleFSIndexProgress, err error) {
  3503  	if k.getIndexer() == nil {
  3504  		return keybase1.SimpleFSIndexProgress{}, errors.New(
  3505  			"Indexing not enabled")
  3506  	}
  3507  
  3508  	p := k.getIndexer().Progress()
  3509  	currProg, overallProg, currTlf, queuedTlfs := p.GetStatus()
  3510  	res.CurrProgress = currProg
  3511  	res.OverallProgress = overallProg
  3512  
  3513  	if currTlf == tlf.NullID && len(queuedTlfs) == 0 {
  3514  		return res, nil
  3515  	}
  3516  
  3517  	// All indexing folders should also be synced.
  3518  	tlfMDs := k.config.KBFSOps().GetAllSyncedTlfMDs(ctx)
  3519  	kbpki, err := k.getKBPKI(ctx)
  3520  	if err != nil {
  3521  		return keybase1.SimpleFSIndexProgress{}, err
  3522  	}
  3523  	session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true)
  3524  	if err != nil {
  3525  		return keybase1.SimpleFSIndexProgress{}, err
  3526  	}
  3527  
  3528  	if currTlf != tlf.NullID {
  3529  		md, ok := tlfMDs[currTlf]
  3530  		if !ok {
  3531  			return keybase1.SimpleFSIndexProgress{}, errors.Errorf(
  3532  				"Folder %s is not currently syncing", currTlf)
  3533  		}
  3534  		f, err := k.getFolder(ctx, currTlf, md, session)
  3535  		if err != nil {
  3536  			return keybase1.SimpleFSIndexProgress{}, err
  3537  		}
  3538  		res.CurrFolder = f
  3539  	}
  3540  
  3541  	res.FoldersLeft = make([]keybase1.Folder, 0, len(queuedTlfs))
  3542  	for _, id := range queuedTlfs {
  3543  		md, ok := tlfMDs[id]
  3544  		if !ok {
  3545  			return keybase1.SimpleFSIndexProgress{}, errors.Errorf(
  3546  				"Folder %s is not currently syncing", id)
  3547  		}
  3548  		f, err := k.getFolder(ctx, id, md, session)
  3549  		if err != nil {
  3550  			return keybase1.SimpleFSIndexProgress{}, err
  3551  		}
  3552  		res.FoldersLeft = append(res.FoldersLeft, f)
  3553  	}
  3554  
  3555  	return res, nil
  3556  }
  3557  
  3558  // SimpleFSMakeTempDirForUpload implements the SimpleFSInterface.
  3559  func (k *SimpleFS) SimpleFSMakeTempDirForUpload(
  3560  	ctx context.Context) (dirPath string, err error) {
  3561  	return k.getUploadManager().makeTempDir()
  3562  }
  3563  
  3564  // SimpleFSStartUpload implements the SimpleFSInterface.
  3565  func (k *SimpleFS) SimpleFSStartUpload(ctx context.Context,
  3566  	arg keybase1.SimpleFSStartUploadArg) (uploadID string, err error) {
  3567  	return k.getUploadManager().start(ctx, arg.SourceLocalPath, arg.TargetParentPath)
  3568  }
  3569  
  3570  // SimpleFSGetUploadStatus implements the SimpleFSInterface.
  3571  func (k *SimpleFS) SimpleFSGetUploadStatus(
  3572  	ctx context.Context) (status []keybase1.UploadState, err error) {
  3573  	return k.getUploadManager().getUploads(), nil
  3574  }
  3575  
  3576  // SimpleFSCancelUpload implements the SimpleFSInterface.
  3577  func (k *SimpleFS) SimpleFSCancelUpload(
  3578  	ctx context.Context, uploadID string) (err error) {
  3579  	return k.getUploadManager().cancel(ctx, uploadID)
  3580  }
  3581  
  3582  // SimpleFSDismissUpload implements the SimpleFSInterface.
  3583  func (k *SimpleFS) SimpleFSDismissUpload(
  3584  	ctx context.Context, uploadID string) (err error) {
  3585  	return k.getUploadManager().dismiss(uploadID)
  3586  }
  3587  
  3588  // SimpleFSCancelJournalUploads implements the SimpleFSInterface.
  3589  func (k *SimpleFS) SimpleFSCancelJournalUploads(
  3590  	ctx context.Context, path keybase1.KBFSPath) (err error) {
  3591  	wrappedPath := keybase1.NewPathWithKbfs(path)
  3592  	ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &wrappedPath, nil)
  3593  	if err != nil {
  3594  		return err
  3595  	}
  3596  	ctx, err = k.startOpWrapContext(k.makeContext(ctx))
  3597  	if err != nil {
  3598  		return err
  3599  	}
  3600  	defer func() {
  3601  		err := libcontext.CleanupCancellationDelayer(ctx)
  3602  		if err != nil {
  3603  			k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err)
  3604  		}
  3605  	}()
  3606  	t, tlfName, _, _, err := remoteTlfAndPath(wrappedPath)
  3607  	if err != nil {
  3608  		return err
  3609  	}
  3610  	kbpki, err := k.getKBPKI(ctx)
  3611  	if err != nil {
  3612  		return err
  3613  	}
  3614  	tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType(
  3615  		ctx, kbpki, k.config.MDOps(), k.config, tlfName, t)
  3616  	if err != nil {
  3617  		return err
  3618  	}
  3619  	tlfID := tlfHandle.TlfID()
  3620  	branch, err := k.branchNameFromPath(ctx, tlfHandle, wrappedPath)
  3621  	if err != nil {
  3622  		return err
  3623  	}
  3624  	return k.config.KBFSOps().CancelUploads(
  3625  		ctx, data.FolderBranch{
  3626  			Tlf:    tlfID,
  3627  			Branch: branch,
  3628  		})
  3629  }
  3630  
  3631  var cacheDirForTest = ""
  3632  
  3633  func setCacheDirForTest(d string) {
  3634  	cacheDirForTest = d
  3635  }
  3636  
  3637  func unsetCacheDirForTest() {
  3638  	cacheDirForTest = ""
  3639  }
  3640  
  3641  func (k *SimpleFS) getCacheDir() string {
  3642  	if len(cacheDirForTest) != 0 {
  3643  		return cacheDirForTest
  3644  	}
  3645  	return k.config.KbEnv().GetCacheDir()
  3646  }
  3647  
  3648  func generateArchiveJobID() (string, error) {
  3649  	buf := make([]byte, 8)
  3650  	err := kbfscrypto.RandRead(buf)
  3651  	if err != nil {
  3652  		return "", err
  3653  	}
  3654  	return fmt.Sprintf("kbfs-archive-job-%s",
  3655  		base64.RawURLEncoding.EncodeToString(buf)), nil
  3656  }
  3657  
  3658  func parseGitRepoForKBFSPath(gitRepo string) (kbfsPath keybase1.KBFSPath, err error) {
  3659  	tlfType, tlfName, repoName, err := kbfsgit.ParseRepo(gitRepo)
  3660  	if err != nil {
  3661  		return keybase1.KBFSPath{}, err
  3662  	}
  3663  	return keybase1.KBFSPath{
  3664  		Path: fmt.Sprintf("/%s/%s/.kbfs_git/%s", tlfType.PathString(), tlfName, repoName),
  3665  	}, nil
  3666  }
  3667  
  3668  var gitKBFSPathRE = regexp.MustCompile(`^\/(private|public|team)\/(.+)\/\.kbfs_git\/(` + libgit.RepoNameRE + `)$`)
  3669  
  3670  func parseKbfsPathForGitRepo(kbfsPath keybase1.KBFSPath) (gitRepo string, repoNameWithDotGit string, ok bool) {
  3671  	match := gitKBFSPathRE.FindStringSubmatch(kbfsPath.Path)
  3672  	if len(match) != 4 {
  3673  		return "", "", false
  3674  	}
  3675  	return fmt.Sprintf("keybase://%s/%s/%s.git", match[1], match[2], match[3]), match[3] + ".git", true
  3676  }
  3677  
  3678  // SimpleFSArchiveStart implements the SimpleFSInterface.
  3679  func (k *SimpleFS) SimpleFSArchiveStart(ctx context.Context,
  3680  	arg keybase1.SimpleFSArchiveStartArg) (jobDesc keybase1.SimpleFSArchiveJobDesc, err error) {
  3681  	ctx = k.makeContext(ctx)
  3682  
  3683  	desc := keybase1.SimpleFSArchiveJobDesc{
  3684  		StartTime:    keybase1.ToTime(time.Now()),
  3685  		OverwriteZip: arg.OverwriteZip,
  3686  	}
  3687  
  3688  	desc.JobID, err = generateArchiveJobID()
  3689  	if err != nil {
  3690  		return keybase1.SimpleFSArchiveJobDesc{}, err
  3691  	}
  3692  	desc.StagingPath = k.getArchiveManager().getStagingPath(ctx, desc.JobID)
  3693  
  3694  	var kbfsPath keybase1.KBFSPath
  3695  	{
  3696  		typ, err := arg.ArchiveJobStartPath.ArchiveJobStartPathType()
  3697  		if err != nil {
  3698  			return keybase1.SimpleFSArchiveJobDesc{},
  3699  				fmt.Errorf("getting ArchiveJobStartPathType error: %v", err)
  3700  		}
  3701  		switch typ {
  3702  		case keybase1.ArchiveJobStartPathType_KBFS:
  3703  			kbfsPath = arg.ArchiveJobStartPath.Kbfs()
  3704  			p, err := splitPathFromKbfsPath(keybase1.NewPathWithKbfs(kbfsPath))
  3705  			if err != nil {
  3706  				return keybase1.SimpleFSArchiveJobDesc{}, err
  3707  			}
  3708  			if len(p) == 0 {
  3709  				return keybase1.SimpleFSArchiveJobDesc{},
  3710  					errors.New("unexpected number of elements from splitPathFromKbfsPath")
  3711  			}
  3712  			desc.TargetName = p[len(p)-1]
  3713  
  3714  		case keybase1.ArchiveJobStartPathType_GIT:
  3715  			kbfsPath, err = parseGitRepoForKBFSPath(arg.ArchiveJobStartPath.Git())
  3716  			if err != nil {
  3717  				return keybase1.SimpleFSArchiveJobDesc{},
  3718  					fmt.Errorf("parsing git repo error: %v", err)
  3719  			}
  3720  		default:
  3721  			return keybase1.SimpleFSArchiveJobDesc{},
  3722  				fmt.Errorf("unexpected ArchiveJobStartPathType: %v", typ)
  3723  		}
  3724  	}
  3725  
  3726  	// Do this separately rather than in the ArchiveJobStartPathType_GIT branch
  3727  	// so it also covers the case where user browses into the `.kbfs_git`
  3728  	// directory and archive.
  3729  	gitRepo, repoName, ok := parseKbfsPathForGitRepo(kbfsPath)
  3730  	if ok {
  3731  		desc.GitRepo = &gitRepo
  3732  		desc.TargetName = repoName
  3733  	}
  3734  
  3735  	desc.ZipFilePath = arg.OutputPath
  3736  	if len(desc.ZipFilePath) == 0 {
  3737  		// No zip file path is given. Assume mobile-like behavior where we
  3738  		// generate a zip file inside the staging path. A share sheet will
  3739  		// allow the user to download the zip file, and when user dismisses the
  3740  		// job, the zip file along with other stuff in the staging path is
  3741  		// deleted.
  3742  		desc.ZipFilePath = filepath.Join(desc.StagingPath, desc.TargetName+".zip")
  3743  	} else if !strings.HasSuffix(desc.ZipFilePath, ".zip") {
  3744  		desc.ZipFilePath += ".zip"
  3745  	}
  3746  
  3747  	// Pin the job to a specific revision so if the TLF changes during the
  3748  	// archive we don't end up mixing two different revisions.
  3749  	{
  3750  		fb, _, err := k.getFolderBranchFromPath(ctx, keybase1.NewPathWithKbfs(kbfsPath))
  3751  		if err != nil {
  3752  			return keybase1.SimpleFSArchiveJobDesc{}, err
  3753  		}
  3754  		if fb == (data.FolderBranch{}) {
  3755  			return keybase1.SimpleFSArchiveJobDesc{}, nil
  3756  		}
  3757  		status, _, err := k.config.KBFSOps().FolderStatus(ctx, fb)
  3758  		if err != nil {
  3759  			return keybase1.SimpleFSArchiveJobDesc{}, err
  3760  		}
  3761  		desc.KbfsPathWithRevision = keybase1.KBFSArchivedPath{
  3762  			Path: kbfsPath.Path,
  3763  			ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision(
  3764  				keybase1.KBFSRevision(status.Revision)),
  3765  		}
  3766  	}
  3767  
  3768  	err = k.getArchiveManager().startJob(ctx, desc)
  3769  	return desc, err
  3770  }
  3771  
  3772  // SimpleFSArchiveCancelOrDismissJob implements the SimpleFSInterface.
  3773  func (k *SimpleFS) SimpleFSArchiveCancelOrDismissJob(ctx context.Context,
  3774  	jobID string) (err error) {
  3775  	ctx = k.makeContext(ctx)
  3776  	return k.getArchiveManager().cancelOrDismissJob(ctx, jobID)
  3777  }
  3778  
  3779  func (k *SimpleFS) getCurrentTLFRevision(
  3780  	ctx context.Context, kbfsPath keybase1.KBFSPath) (
  3781  	keybase1.KBFSRevision, error) {
  3782  	fb, _, err := k.getFolderBranchFromPath(ctx,
  3783  		keybase1.NewPathWithKbfs(kbfsPath))
  3784  	if err != nil {
  3785  		return 0, err
  3786  	}
  3787  	if fb == (data.FolderBranch{}) {
  3788  		return 0, nil
  3789  	}
  3790  	status, _, err := k.config.KBFSOps().FolderStatus(ctx, fb)
  3791  	if err != nil {
  3792  		return 0, err
  3793  	}
  3794  	return keybase1.KBFSRevision(status.Revision), nil
  3795  }
  3796  
  3797  // SimpleFSArchiveCheckArchive implements the SimpleFSInterface.
  3798  func (k *SimpleFS) SimpleFSArchiveCheckArchive(ctx context.Context,
  3799  	archiveZipFilePath string) (result keybase1.SimpleFSArchiveCheckArchiveResult, err error) {
  3800  	ctx = k.makeContext(ctx)
  3801  	result.Desc, result.PathsWithIssues, err = k.getArchiveManager().checkArchive(ctx, archiveZipFilePath)
  3802  	if err != nil {
  3803  		return keybase1.SimpleFSArchiveCheckArchiveResult{}, err
  3804  	}
  3805  	result.CurrentTLFRevision, err = k.getCurrentTLFRevision(ctx,
  3806  		keybase1.KBFSPath{Path: result.Desc.KbfsPathWithRevision.Path})
  3807  	if err != nil {
  3808  		return keybase1.SimpleFSArchiveCheckArchiveResult{}, err
  3809  	}
  3810  	return result, nil
  3811  }
  3812  
  3813  func (k *SimpleFS) archiveStateToStatus(ctx context.Context,
  3814  	state keybase1.SimpleFSArchiveState, errorStates map[string]errorState) (
  3815  	status keybase1.SimpleFSArchiveStatus, err error) {
  3816  	status = keybase1.SimpleFSArchiveStatus{
  3817  		LastUpdated: state.LastUpdated,
  3818  		Jobs:        make([]keybase1.SimpleFSArchiveJobStatus, 0, len(state.Jobs)),
  3819  	}
  3820  	for jobID, stateJob := range state.Jobs {
  3821  		statusJob := keybase1.SimpleFSArchiveJobStatus{
  3822  			Desc:        stateJob.Desc.DeepCopy(),
  3823  			TotalCount:  len(stateJob.Manifest),
  3824  			Phase:       stateJob.Phase,
  3825  			BytesCopied: stateJob.BytesCopied,
  3826  			BytesZipped: stateJob.BytesZipped,
  3827  			BytesTotal:  stateJob.BytesTotal,
  3828  		}
  3829  		for _, item := range stateJob.Manifest {
  3830  			switch item.State {
  3831  			case keybase1.SimpleFSFileArchiveState_ToDo:
  3832  				statusJob.TodoCount++
  3833  			case keybase1.SimpleFSFileArchiveState_InProgress:
  3834  				statusJob.InProgressCount++
  3835  			case keybase1.SimpleFSFileArchiveState_Complete:
  3836  				statusJob.CompleteCount++
  3837  			case keybase1.SimpleFSFileArchiveState_Skipped:
  3838  				statusJob.SkippedCount++
  3839  			}
  3840  		}
  3841  		if errState, ok := errorStates[jobID]; ok {
  3842  			statusJob.Error = &keybase1.SimpleFSArchiveJobErrorState{
  3843  				Error:     errState.err.Error(),
  3844  				NextRetry: keybase1.ToTime(errState.nextRetry),
  3845  			}
  3846  		}
  3847  		status.Jobs = append(status.Jobs, statusJob)
  3848  	}
  3849  	sort.Slice(status.Jobs, func(i, j int) bool {
  3850  		return status.Jobs[i].Desc.StartTime.Before(status.Jobs[j].Desc.StartTime)
  3851  	})
  3852  	return status, nil
  3853  }
  3854  
  3855  // SimpleFSGetArchiveStatus implements the SimpleFSInterface.
  3856  func (k *SimpleFS) SimpleFSGetArchiveStatus(ctx context.Context) (
  3857  	status keybase1.SimpleFSArchiveStatus, err error) {
  3858  	ctx = k.makeContext(ctx)
  3859  	state, errorStates := k.getArchiveManager().getCurrentState(ctx)
  3860  	return k.archiveStateToStatus(ctx, state, errorStates)
  3861  }
  3862  
  3863  // SimpleFSGetArchiveJobFreshness implements the SimpleFSInterface.
  3864  func (k *SimpleFS) SimpleFSGetArchiveJobFreshness(ctx context.Context, jobID string) (keybase1.SimpleFSArchiveJobFreshness, error) {
  3865  	ctx = k.makeContext(ctx)
  3866  	state, _ := k.getArchiveManager().getCurrentState(ctx)
  3867  	stateJob, ok := state.Jobs[jobID]
  3868  	if !ok {
  3869  		return keybase1.SimpleFSArchiveJobFreshness{}, fmt.Errorf("job not found: %s", jobID)
  3870  	}
  3871  	rev, err := k.getCurrentTLFRevision(ctx,
  3872  		keybase1.KBFSPath{Path: stateJob.Desc.KbfsPathWithRevision.Path})
  3873  	if err != nil {
  3874  		return keybase1.SimpleFSArchiveJobFreshness{}, err
  3875  	}
  3876  	return keybase1.SimpleFSArchiveJobFreshness{
  3877  		CurrentTLFRevision: rev,
  3878  	}, nil
  3879  }
  3880  
  3881  // SimpleFSArchiveAllFiles implements the SimpleFSInterface.
  3882  func (k *SimpleFS) SimpleFSArchiveAllFiles(
  3883  	ctx context.Context, arg keybase1.SimpleFSArchiveAllFilesArg) (
  3884  	res keybase1.SimpleFSArchiveAllFilesResult, err error) {
  3885  	var startArgs []keybase1.SimpleFSArchiveStartArg
  3886  	{
  3887  		fl, err := k.favoriteList(ctx, tlf.Private)
  3888  		if err != nil {
  3889  			return keybase1.SimpleFSArchiveAllFilesResult{}, err
  3890  		}
  3891  		for _, item := range fl {
  3892  			startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{
  3893  				ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithKbfs(keybase1.KBFSPath{
  3894  					Path: fmt.Sprintf("/private/%s", item.Name),
  3895  				}),
  3896  				OutputPath:   filepath.Join(arg.OutputDir, "kbfs-private-"+item.Name+".zip"),
  3897  				OverwriteZip: arg.OverwriteZip,
  3898  			})
  3899  		}
  3900  
  3901  		fl, err = k.favoriteList(ctx, tlf.Public)
  3902  		if err != nil {
  3903  			return keybase1.SimpleFSArchiveAllFilesResult{}, err
  3904  		}
  3905  		for _, item := range fl {
  3906  			p := fmt.Sprintf("/public/%s", item.Name)
  3907  			if arg.IncludePublicReadonly || item.Writable {
  3908  				startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{
  3909  					ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithKbfs(keybase1.KBFSPath{
  3910  						Path: p,
  3911  					}),
  3912  					OutputPath:   filepath.Join(arg.OutputDir, "kbfs-public-"+item.Name+".zip"),
  3913  					OverwriteZip: arg.OverwriteZip,
  3914  				})
  3915  			} else {
  3916  				res.SkippedTLFPaths = append(res.SkippedTLFPaths, p)
  3917  			}
  3918  		}
  3919  
  3920  		fl, err = k.favoriteList(ctx, tlf.SingleTeam)
  3921  		if err != nil {
  3922  			return keybase1.SimpleFSArchiveAllFilesResult{}, err
  3923  		}
  3924  		for _, item := range fl {
  3925  			startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{
  3926  				ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithKbfs(keybase1.KBFSPath{
  3927  					Path: fmt.Sprintf("/team/%s", item.Name),
  3928  				}),
  3929  				OutputPath:   filepath.Join(arg.OutputDir, "kbfs-team-"+item.Name+".zip"),
  3930  				OverwriteZip: arg.OverwriteZip,
  3931  			})
  3932  		}
  3933  	}
  3934  
  3935  	res.TlfPathToError = make(map[string]string)
  3936  	res.TlfPathToJobDesc = make(map[string]keybase1.SimpleFSArchiveJobDesc)
  3937  	for _, a := range startArgs {
  3938  		jobDesc, err := k.SimpleFSArchiveStart(ctx, a)
  3939  		if err != nil {
  3940  			res.TlfPathToError[a.ArchiveJobStartPath.Kbfs().Path] = err.Error()
  3941  		} else {
  3942  			res.TlfPathToJobDesc[a.ArchiveJobStartPath.Kbfs().Path] = jobDesc
  3943  		}
  3944  	}
  3945  
  3946  	return res, nil
  3947  }
  3948  
  3949  func (k *SimpleFS) getAllGitMetadata(ctx context.Context) (res []keybase1.GitRepoResult, err error) {
  3950  	ks := k.config.KeybaseService()
  3951  	if ks == nil {
  3952  		k.log.CWarningf(ctx, "k.getAllGitMetadata: ks is nil")
  3953  		return nil, errors.New("ks is nil")
  3954  	}
  3955  	rc := ks.GetKeybaseDaemonRawClient()
  3956  	if rc == nil {
  3957  		k.log.CWarningf(ctx, "k.getAllGitMetadata: rawClient is nil")
  3958  		return nil, errors.New("rawClient is nil")
  3959  	}
  3960  	client := keybase1.GitClient{Cli: rc}
  3961  	res, err = client.GetAllGitMetadata(ctx)
  3962  	if err != nil {
  3963  		k.log.CWarningf(ctx, "k.getAllGitMetadata: client.GetAllGitMetadata error: %v", err)
  3964  		return nil, err
  3965  	}
  3966  	return res, nil
  3967  }
  3968  
  3969  // SimpleFSArchiveAllGitRepos implements the SimpleFSInterface.
  3970  func (k *SimpleFS) SimpleFSArchiveAllGitRepos(
  3971  	ctx context.Context, arg keybase1.SimpleFSArchiveAllGitReposArg) (
  3972  	res keybase1.SimpleFSArchiveAllGitReposResult, err error) {
  3973  	allGitMetadataResult, err := k.getAllGitMetadata(ctx)
  3974  	if err != nil {
  3975  		return keybase1.SimpleFSArchiveAllGitReposResult{}, err
  3976  	}
  3977  
  3978  	var startArgs []keybase1.SimpleFSArchiveStartArg
  3979  	for _, gitRepoResult := range allGitMetadataResult {
  3980  		state, err := gitRepoResult.State()
  3981  		if err != nil {
  3982  			return keybase1.SimpleFSArchiveAllGitReposResult{},
  3983  				fmt.Errorf("gitRepoResult.State() error: %v", err)
  3984  		}
  3985  		switch state {
  3986  		case keybase1.GitRepoResultState_OK:
  3987  			gitRepoInfo := gitRepoResult.Ok()
  3988  			folderTypeStr, err := func() (string, error) {
  3989  				switch gitRepoInfo.Folder.FolderType {
  3990  				case keybase1.FolderType_PRIVATE:
  3991  					return "private", nil
  3992  				case keybase1.FolderType_TEAM:
  3993  					return "team", nil
  3994  				default:
  3995  					return "", fmt.Errorf("unexpected FolderType: %v", gitRepoInfo.Folder.FolderType)
  3996  				}
  3997  			}()
  3998  			if err != nil {
  3999  				return keybase1.SimpleFSArchiveAllGitReposResult{}, err
  4000  			}
  4001  			startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{
  4002  				ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithGit(gitRepoInfo.RepoUrl),
  4003  				OutputPath:          filepath.Join(arg.OutputDir, "git-"+folderTypeStr+"-"+string(gitRepoInfo.LocalMetadata.RepoName)+".git.zip"),
  4004  				OverwriteZip:        arg.OverwriteZip,
  4005  			})
  4006  		case keybase1.GitRepoResultState_ERR:
  4007  			return keybase1.SimpleFSArchiveAllGitReposResult{},
  4008  				fmt.Errorf("getRepoResult result error: %v", gitRepoResult.Err())
  4009  		default:
  4010  			return keybase1.SimpleFSArchiveAllGitReposResult{}, fmt.Errorf("unexpected gitRepoResult.State() %v", state)
  4011  
  4012  		}
  4013  	}
  4014  
  4015  	res.GitRepoToError = make(map[string]string)
  4016  	res.GitRepoToJobDesc = make(map[string]keybase1.SimpleFSArchiveJobDesc)
  4017  	for _, a := range startArgs {
  4018  		jobDesc, err := k.SimpleFSArchiveStart(ctx, a)
  4019  		if err != nil {
  4020  			res.GitRepoToError[a.ArchiveJobStartPath.Git()] = err.Error()
  4021  		} else {
  4022  			res.GitRepoToJobDesc[a.ArchiveJobStartPath.Git()] = jobDesc
  4023  		}
  4024  	}
  4025  
  4026  	return res, nil
  4027  }
  4028  
  4029  func (k *SimpleFS) notifyUIArchiveStateChange(ctx context.Context,
  4030  	state keybase1.SimpleFSArchiveState, errorStates map[string]errorState) {
  4031  	ks := k.config.KeybaseService()
  4032  	if ks == nil {
  4033  		k.log.CWarningf(ctx,
  4034  			"k.notifyUIArchiveStateChange: skipping notification because KeybaseService() is nil")
  4035  		return
  4036  	}
  4037  	rc := ks.GetKeybaseDaemonRawClient()
  4038  	if rc == nil {
  4039  		k.log.CWarningf(ctx,
  4040  			"k.notifyUIArchiveStateChange: skipping notification because rawClient is nil")
  4041  		return
  4042  	}
  4043  	client := keybase1.NotifySimpleFSClient{Cli: rc}
  4044  
  4045  	status, err := k.archiveStateToStatus(ctx, state, errorStates)
  4046  	if err != nil {
  4047  		k.log.CWarningf(ctx,
  4048  			"k.archiveStateToStatus error: %v", err)
  4049  	}
  4050  	err = client.SimpleFSArchiveStatusChanged(ctx, status)
  4051  	if err != nil {
  4052  		k.log.CWarningf(ctx,
  4053  			"sending SimpleFSArchiveStatusChanged notification error: %v", err)
  4054  	}
  4055  }
  4056  
  4057  // Shutdown shuts down SimpleFS.
  4058  func (k *SimpleFS) Shutdown(ctx context.Context) error {
  4059  	k.shutdownLock.Lock()
  4060  	defer k.shutdownLock.Unlock()
  4061  
  4062  	k.archiveManager.shutdown(ctx)
  4063  	if k.indexer == nil {
  4064  		return nil
  4065  	}
  4066  	return k.indexer.Shutdown(ctx)
  4067  }