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