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