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 }