github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/test/engine_libkbfs.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 test 6 7 import ( 8 "fmt" 9 "os" 10 "path/filepath" 11 "testing" 12 "time" 13 14 "github.com/keybase/client/go/kbfs/data" 15 "github.com/keybase/client/go/kbfs/idutil" 16 "github.com/keybase/client/go/kbfs/ioutil" 17 "github.com/keybase/client/go/kbfs/kbfsmd" 18 "github.com/keybase/client/go/kbfs/libcontext" 19 "github.com/keybase/client/go/kbfs/libfs" 20 "github.com/keybase/client/go/kbfs/libkbfs" 21 "github.com/keybase/client/go/kbfs/tlf" 22 "github.com/keybase/client/go/kbfs/tlfhandle" 23 kbname "github.com/keybase/client/go/kbun" 24 "github.com/keybase/client/go/logger" 25 "github.com/keybase/client/go/protocol/keybase1" 26 "github.com/pkg/errors" 27 "golang.org/x/net/context" 28 ) 29 30 // LibKBFS implements the Engine interface for direct test harness usage of libkbfs. 31 type LibKBFS struct { 32 // hack: hold references on behalf of the test harness 33 refs map[libkbfs.Config]map[libkbfs.Node]bool 34 // channels used to re-enable updates if disabled 35 updateChannels map[libkbfs.Config]map[data.FolderBranch]chan<- struct{} 36 // test object, for logging. 37 tb testing.TB 38 // timeout for all KBFS calls 39 opTimeout time.Duration 40 // journal directory 41 journalDir string 42 } 43 44 // Check that LibKBFS fully implements the Engine interface. 45 var _ Engine = (*LibKBFS)(nil) 46 47 // Name returns the name of the Engine. 48 func (k *LibKBFS) Name() string { 49 return "libkbfs" 50 } 51 52 // InitTest implements the Engine interface. 53 func (k *LibKBFS) InitTest(ver kbfsmd.MetadataVer, 54 blockSize int64, blockChangeSize int64, batchSize int, bwKBps int, 55 opTimeout time.Duration, users []kbname.NormalizedUsername, 56 teams, implicitTeams teamMap, clock libkbfs.Clock, 57 journal bool) map[kbname.NormalizedUsername]User { 58 userMap := make(map[kbname.NormalizedUsername]User) 59 // create the first user specially 60 config := libkbfs.MakeTestConfigOrBust(k.tb, users...) 61 config.SetMetadataVersion(ver) 62 63 setBlockSizes(k.tb, config, blockSize, blockChangeSize) 64 if batchSize > 0 { 65 config.SetBGFlushDirOpBatchSize(batchSize) 66 } 67 maybeSetBw(k.tb, config, bwKBps) 68 k.opTimeout = opTimeout 69 70 config.SetClock(clock) 71 userMap[users[0]] = config 72 k.refs[config] = make(map[libkbfs.Node]bool) 73 k.updateChannels[config] = make(map[data.FolderBranch]chan<- struct{}) 74 75 // create the rest of the users as copies of the original config 76 for _, name := range users[1:] { 77 c := libkbfs.ConfigAsUser(config, name) 78 setBlockSizes(k.tb, c, blockSize, blockChangeSize) 79 if batchSize > 0 { 80 c.SetBGFlushDirOpBatchSize(batchSize) 81 } 82 c.SetClock(clock) 83 userMap[name] = c 84 k.refs[c] = make(map[libkbfs.Node]bool) 85 k.updateChannels[c] = make(map[data.FolderBranch]chan<- struct{}) 86 } 87 88 if journal { 89 jdir, err := os.MkdirTemp(os.TempDir(), "kbfs_journal") 90 if err != nil { 91 k.tb.Fatalf("Couldn't enable journaling: %v", err) 92 } 93 k.journalDir = jdir 94 k.tb.Logf("Journal directory: %s", k.journalDir) 95 for name, c := range userMap { 96 config := c.(*libkbfs.ConfigLocal) 97 journalRoot := filepath.Join(jdir, name.String()) 98 err = config.EnableDiskLimiter(journalRoot) 99 if err != nil { 100 panic(fmt.Sprintf("No disk limiter for %s: %+v", name, err)) 101 } 102 err = config.EnableJournaling(context.Background(), journalRoot, 103 libkbfs.TLFJournalBackgroundWorkEnabled) 104 if err != nil { 105 panic(fmt.Sprintf("Couldn't enable journaling: %+v", err)) 106 } 107 jManager, err := libkbfs.GetJournalManager(config) 108 if err != nil { 109 panic(fmt.Sprintf("No journal server for %s: %+v", name, err)) 110 } 111 err = jManager.DisableAuto(context.Background()) 112 if err != nil { 113 panic(fmt.Sprintf("Couldn't disable journaling: %+v", err)) 114 } 115 } 116 } 117 118 for _, u := range userMap { 119 c := u.(libkbfs.Config) 120 makeTeams(k.tb, c, k, teams, userMap) 121 makeImplicitTeams(k.tb, c, k, implicitTeams, userMap) 122 } 123 124 return userMap 125 } 126 127 const ( 128 // CtxOpID is the display name for the unique operation test ID tag. 129 CtxOpID = "TID" 130 131 // CtxOpUser is the display name for the user tag. 132 CtxOpUser = "User" 133 ) 134 135 // CtxTagKey is the type used for unique context tags 136 type CtxTagKey int 137 138 const ( 139 // CtxIDKey is the type of the tag for unique operation IDs. 140 CtxIDKey CtxTagKey = iota 141 142 // CtxUserKey is the type of the user tag. 143 CtxUserKey 144 ) 145 146 func (k *LibKBFS) newContext(u User) (context.Context, context.CancelFunc) { 147 ctx := context.Background() 148 149 config, ok := u.(libkbfs.Config) 150 if !ok { 151 panic("passed parameter isn't a config object") 152 } 153 session, err := config.KBPKI().GetCurrentSession(ctx) 154 if err != nil { 155 panic(err) 156 } 157 158 var cancel context.CancelFunc 159 if k.opTimeout > 0 { 160 ctx, cancel = context.WithTimeout(ctx, k.opTimeout) 161 } else { 162 ctx, cancel = context.WithCancel(ctx) 163 } 164 165 id, errRandomRequestID := libkbfs.MakeRandomRequestID() 166 ctx, err = libcontext.NewContextWithCancellationDelayer(libcontext.NewContextReplayable( 167 ctx, func(ctx context.Context) context.Context { 168 logTags := logger.CtxLogTags{ 169 CtxIDKey: CtxOpID, 170 CtxUserKey: CtxOpUser, 171 } 172 ctx = logger.NewContextWithLogTags(ctx, logTags) 173 174 // Add a unique ID to this context, identifying a particular 175 // request. 176 if errRandomRequestID == nil { 177 ctx = context.WithValue(ctx, CtxIDKey, id) 178 } 179 180 ctx = context.WithValue(ctx, CtxUserKey, session.Name) 181 182 return ctx 183 })) 184 if err != nil { 185 panic(err) 186 } 187 188 return ctx, func() { 189 err := libcontext.CleanupCancellationDelayer(ctx) 190 if err != nil { 191 panic(err) 192 } 193 cancel() 194 } 195 } 196 197 // GetUID implements the Engine interface. 198 func (k *LibKBFS) GetUID(u User) (uid keybase1.UID) { 199 config, ok := u.(libkbfs.Config) 200 if !ok { 201 panic("passed parameter isn't a config object") 202 } 203 var err error 204 ctx, cancel := k.newContext(u) 205 defer cancel() 206 session, err := config.KBPKI().GetCurrentSession(ctx) 207 if err != nil { 208 panic(err.Error()) 209 } 210 return session.UID 211 } 212 213 func parseTlfHandle( 214 ctx context.Context, kbpki libkbfs.KBPKI, mdOps libkbfs.MDOps, 215 osg idutil.OfflineStatusGetter, tlfName string, t tlf.Type) ( 216 h *tlfhandle.Handle, err error) { 217 // Limit to one non-canonical name for now. 218 outer: 219 for i := 0; i < 2; i++ { 220 h, err = tlfhandle.ParseHandle(ctx, kbpki, mdOps, osg, tlfName, t) 221 switch err := errors.Cause(err).(type) { 222 case nil: 223 break outer 224 case idutil.TlfNameNotCanonical: 225 tlfName = err.NameToTry 226 default: 227 return nil, err 228 } 229 } 230 if err != nil { 231 return nil, err 232 } 233 return h, nil 234 } 235 236 // GetFavorites implements the Engine interface. 237 func (k *LibKBFS) GetFavorites(u User, t tlf.Type) (map[string]bool, error) { 238 config := u.(*libkbfs.ConfigLocal) 239 ctx, cancel := k.newContext(u) 240 defer cancel() 241 favorites, err := config.KBFSOps().GetFavorites(ctx) 242 if err != nil { 243 return nil, err 244 } 245 favoritesMap := make(map[string]bool) 246 for _, f := range favorites { 247 if f.Type != t { 248 continue 249 } 250 favoritesMap[f.Name] = true 251 } 252 return favoritesMap, nil 253 } 254 255 func (k *LibKBFS) getRootDir( 256 u User, tlfName string, t tlf.Type, branch data.BranchName, 257 expectedCanonicalTlfName string) (dir Node, err error) { 258 config := u.(*libkbfs.ConfigLocal) 259 260 ctx, cancel := k.newContext(u) 261 defer cancel() 262 h, err := parseTlfHandle( 263 ctx, config.KBPKI(), config.MDOps(), config, tlfName, t) 264 if err != nil { 265 return nil, err 266 } 267 268 if string(h.GetCanonicalName()) != expectedCanonicalTlfName { 269 return nil, fmt.Errorf("Expected canonical TLF name %s, got %s", 270 expectedCanonicalTlfName, h.GetCanonicalName()) 271 } 272 273 if h.IsLocalConflict() { 274 b, ok := data.MakeConflictBranchName(h) 275 if ok { 276 branch = b 277 } 278 } 279 280 if branch == data.MasterBranch { 281 dir, _, err = config.KBFSOps().GetOrCreateRootNode(ctx, h, branch) 282 } else { 283 dir, _, err = config.KBFSOps().GetRootNode(ctx, h, branch) 284 } 285 if err != nil { 286 return nil, err 287 } 288 289 k.refs[config][dir.(libkbfs.Node)] = true 290 return dir, nil 291 } 292 293 // GetRootDir implements the Engine interface. 294 func (k *LibKBFS) GetRootDir( 295 u User, tlfName string, t tlf.Type, expectedCanonicalTlfName string) ( 296 dir Node, err error) { 297 return k.getRootDir( 298 u, tlfName, t, data.MasterBranch, expectedCanonicalTlfName) 299 } 300 301 // GetRootDirAtRevision implements the Engine interface. 302 func (k *LibKBFS) GetRootDirAtRevision( 303 u User, tlfName string, t tlf.Type, rev kbfsmd.Revision, 304 expectedCanonicalTlfName string) (dir Node, err error) { 305 return k.getRootDir( 306 u, tlfName, t, data.MakeRevBranchName(rev), expectedCanonicalTlfName) 307 } 308 309 // GetRootDirAtTimeString implements the Engine interface. 310 func (k *LibKBFS) GetRootDirAtTimeString( 311 u User, tlfName string, t tlf.Type, timeString string, 312 expectedCanonicalTlfName string) (dir Node, err error) { 313 config := u.(*libkbfs.ConfigLocal) 314 ctx, cancel := k.newContext(u) 315 defer cancel() 316 h, err := parseTlfHandle( 317 ctx, config.KBPKI(), config.MDOps(), config, tlfName, t) 318 if err != nil { 319 return nil, err 320 } 321 322 rev, err := libfs.RevFromTimeString(ctx, config, h, timeString) 323 if err != nil { 324 return nil, err 325 } 326 327 return k.getRootDir( 328 u, tlfName, t, data.MakeRevBranchName(rev), expectedCanonicalTlfName) 329 } 330 331 // GetRootDirAtRelTimeString implements the Engine interface. 332 func (k *LibKBFS) GetRootDirAtRelTimeString( 333 u User, tlfName string, t tlf.Type, relTimeString string, 334 expectedCanonicalTlfName string) (dir Node, err error) { 335 config := u.(*libkbfs.ConfigLocal) 336 ctx, cancel := k.newContext(u) 337 defer cancel() 338 h, err := parseTlfHandle( 339 ctx, config.KBPKI(), config.MDOps(), config, tlfName, t) 340 if err != nil { 341 return nil, err 342 } 343 344 rev, err := libfs.RevFromRelativeTimeString(ctx, config, h, relTimeString) 345 if err != nil { 346 return nil, err 347 } 348 349 return k.getRootDir( 350 u, tlfName, t, data.MakeRevBranchName(rev), expectedCanonicalTlfName) 351 } 352 353 // CreateDir implements the Engine interface. 354 func (k *LibKBFS) CreateDir(u User, parentDir Node, name string) (dir Node, err error) { 355 config := u.(*libkbfs.ConfigLocal) 356 kbfsOps := config.KBFSOps() 357 ctx, cancel := k.newContext(u) 358 defer cancel() 359 n := parentDir.(libkbfs.Node) 360 dir, _, err = kbfsOps.CreateDir(ctx, n, n.ChildName(name)) 361 if err != nil { 362 return dir, err 363 } 364 k.refs[config][dir.(libkbfs.Node)] = true 365 return dir, nil 366 } 367 368 // CreateFile implements the Engine interface. 369 func (k *LibKBFS) CreateFile(u User, parentDir Node, name string) (file Node, err error) { 370 config := u.(*libkbfs.ConfigLocal) 371 kbfsOps := config.KBFSOps() 372 ctx, cancel := k.newContext(u) 373 defer cancel() 374 n := parentDir.(libkbfs.Node) 375 file, _, err = kbfsOps.CreateFile( 376 ctx, n, n.ChildName(name), false, libkbfs.NoExcl) 377 if err != nil { 378 return file, err 379 } 380 k.refs[config][file.(libkbfs.Node)] = true 381 return file, nil 382 } 383 384 // CreateFileExcl implements the Engine interface. 385 func (k *LibKBFS) CreateFileExcl(u User, parentDir Node, name string) (file Node, err error) { 386 config := u.(*libkbfs.ConfigLocal) 387 kbfsOps := config.KBFSOps() 388 ctx, cancel := k.newContext(u) 389 defer cancel() 390 n := parentDir.(libkbfs.Node) 391 file, _, err = kbfsOps.CreateFile( 392 ctx, n, n.ChildName(name), false, libkbfs.WithExcl) 393 if err != nil { 394 return nil, err 395 } 396 k.refs[config][file.(libkbfs.Node)] = true 397 return file, nil 398 } 399 400 // CreateLink implements the Engine interface. 401 func (k *LibKBFS) CreateLink( 402 u User, parentDir Node, fromName, toPath string) (err error) { 403 config := u.(*libkbfs.ConfigLocal) 404 kbfsOps := config.KBFSOps() 405 ctx, cancel := k.newContext(u) 406 defer cancel() 407 n := parentDir.(libkbfs.Node) 408 _, err = kbfsOps.CreateLink( 409 ctx, n, n.ChildName(fromName), n.ChildName(toPath)) 410 return err 411 } 412 413 // RemoveDir implements the Engine interface. 414 func (k *LibKBFS) RemoveDir(u User, dir Node, name string) (err error) { 415 kbfsOps := u.(*libkbfs.ConfigLocal).KBFSOps() 416 ctx, cancel := k.newContext(u) 417 defer cancel() 418 n := dir.(libkbfs.Node) 419 return kbfsOps.RemoveDir(ctx, n, n.ChildName(name)) 420 } 421 422 // RemoveEntry implements the Engine interface. 423 func (k *LibKBFS) RemoveEntry(u User, dir Node, name string) (err error) { 424 kbfsOps := u.(*libkbfs.ConfigLocal).KBFSOps() 425 ctx, cancel := k.newContext(u) 426 defer cancel() 427 n := dir.(libkbfs.Node) 428 return kbfsOps.RemoveEntry(ctx, n, n.ChildName(name)) 429 } 430 431 // Rename implements the Engine interface. 432 func (k *LibKBFS) Rename(u User, srcDir Node, srcName string, 433 dstDir Node, dstName string) (err error) { 434 kbfsOps := u.(*libkbfs.ConfigLocal).KBFSOps() 435 ctx, cancel := k.newContext(u) 436 defer cancel() 437 srcN := srcDir.(libkbfs.Node) 438 dstN := dstDir.(libkbfs.Node) 439 return kbfsOps.Rename( 440 ctx, srcN, srcN.ChildName(srcName), dstN, dstN.ChildName(dstName)) 441 } 442 443 // WriteFile implements the Engine interface. 444 func (k *LibKBFS) WriteFile(u User, file Node, data []byte, off int64, sync bool) (err error) { 445 kbfsOps := u.(*libkbfs.ConfigLocal).KBFSOps() 446 ctx, cancel := k.newContext(u) 447 defer cancel() 448 err = kbfsOps.Write(ctx, file.(libkbfs.Node), data, off) 449 if err != nil { 450 return err 451 } 452 if sync { 453 ctx, cancel := k.newContext(u) 454 defer cancel() 455 err = kbfsOps.SyncAll(ctx, file.(libkbfs.Node).GetFolderBranch()) 456 } 457 return err 458 } 459 460 // TruncateFile implements the Engine interface. 461 func (k *LibKBFS) TruncateFile(u User, file Node, size uint64, sync bool) (err error) { 462 kbfsOps := u.(*libkbfs.ConfigLocal).KBFSOps() 463 ctx, cancel := k.newContext(u) 464 defer cancel() 465 err = kbfsOps.Truncate(ctx, file.(libkbfs.Node), size) 466 if err != nil { 467 return err 468 } 469 if sync { 470 ctx, cancel := k.newContext(u) 471 defer cancel() 472 err = kbfsOps.SyncAll(ctx, file.(libkbfs.Node).GetFolderBranch()) 473 } 474 return err 475 } 476 477 // ReadFile implements the Engine interface. 478 func (k *LibKBFS) ReadFile(u User, file Node, off int64, buf []byte) (length int, err error) { 479 kbfsOps := u.(*libkbfs.ConfigLocal).KBFSOps() 480 var numRead int64 481 ctx, cancel := k.newContext(u) 482 defer cancel() 483 numRead, err = kbfsOps.Read(ctx, file.(libkbfs.Node), buf, off) 484 if err != nil { 485 return 0, err 486 } 487 return int(numRead), nil 488 } 489 490 type libkbfsSymNode struct { 491 parentDir Node 492 name string 493 } 494 495 // Lookup implements the Engine interface. 496 func (k *LibKBFS) Lookup(u User, parentDir Node, name string) (file Node, symPath string, err error) { 497 config := u.(*libkbfs.ConfigLocal) 498 kbfsOps := config.KBFSOps() 499 ctx, cancel := k.newContext(u) 500 defer cancel() 501 n := parentDir.(libkbfs.Node) 502 file, ei, err := kbfsOps.Lookup(ctx, n, n.ChildName(name)) 503 if err != nil { 504 return file, symPath, err 505 } 506 if file != nil { 507 k.refs[config][file.(libkbfs.Node)] = true 508 } 509 if ei.Type == data.Sym { 510 symPath = ei.SymPath 511 } 512 if file == nil { 513 // For symlnks, return a special kind of node that can be used 514 // to look up stats about the symlink. 515 return libkbfsSymNode{parentDir, name}, symPath, nil 516 } 517 return file, symPath, nil 518 } 519 520 // GetDirChildrenTypes implements the Engine interface. 521 func (k *LibKBFS) GetDirChildrenTypes(u User, parentDir Node) (childrenTypes map[string]string, err error) { 522 kbfsOps := u.(*libkbfs.ConfigLocal).KBFSOps() 523 ctx, cancel := k.newContext(u) 524 defer cancel() 525 entries, err := kbfsOps.GetDirChildren(ctx, parentDir.(libkbfs.Node)) 526 if err != nil { 527 return childrenTypes, err 528 } 529 childrenTypes = make(map[string]string) 530 for name, entryInfo := range entries { 531 childrenTypes[name.Plaintext()] = entryInfo.Type.String() 532 } 533 return childrenTypes, nil 534 } 535 536 // SetEx implements the Engine interface. 537 func (k *LibKBFS) SetEx(u User, file Node, ex bool) (err error) { 538 config := u.(*libkbfs.ConfigLocal) 539 kbfsOps := config.KBFSOps() 540 ctx, cancel := k.newContext(u) 541 defer cancel() 542 return kbfsOps.SetEx(ctx, file.(libkbfs.Node), ex) 543 } 544 545 // SetMtime implements the Engine interface. 546 func (k *LibKBFS) SetMtime(u User, file Node, mtime time.Time) (err error) { 547 config := u.(*libkbfs.ConfigLocal) 548 kbfsOps := config.KBFSOps() 549 ctx, cancel := k.newContext(u) 550 defer cancel() 551 return kbfsOps.SetMtime(ctx, file.(libkbfs.Node), &mtime) 552 } 553 554 // SyncAll implements the Engine interface. 555 func (k *LibKBFS) SyncAll( 556 u User, tlfName string, t tlf.Type) (err error) { 557 config := u.(*libkbfs.ConfigLocal) 558 559 ctx, cancel := k.newContext(u) 560 defer cancel() 561 dir, err := getRootNode(ctx, config, tlfName, t) 562 if err != nil { 563 return err 564 } 565 566 return config.KBFSOps().SyncAll(ctx, dir.GetFolderBranch()) 567 } 568 569 // GetMtime implements the Engine interface. 570 func (k *LibKBFS) GetMtime(u User, file Node) (mtime time.Time, err error) { 571 config := u.(*libkbfs.ConfigLocal) 572 kbfsOps := config.KBFSOps() 573 var info data.EntryInfo 574 ctx, cancel := k.newContext(u) 575 defer cancel() 576 if node, ok := file.(libkbfs.Node); ok { 577 info, err = kbfsOps.Stat(ctx, node) 578 } else if node, ok := file.(libkbfsSymNode); ok { 579 // Stat doesn't work for symlinks, so use lookup 580 n := node.parentDir.(libkbfs.Node) 581 _, info, err = kbfsOps.Lookup(ctx, n, n.ChildName(node.name)) 582 } 583 if err != nil { 584 return time.Time{}, err 585 } 586 return time.Unix(0, info.Mtime), nil 587 } 588 589 // GetPrevRevisions implements the Engine interface. 590 func (k *LibKBFS) GetPrevRevisions(u User, file Node) ( 591 revs data.PrevRevisions, err error) { 592 config := u.(*libkbfs.ConfigLocal) 593 kbfsOps := config.KBFSOps() 594 var info data.EntryInfo 595 ctx, cancel := k.newContext(u) 596 defer cancel() 597 if node, ok := file.(libkbfs.Node); ok { 598 info, err = kbfsOps.Stat(ctx, node) 599 } else if node, ok := file.(libkbfsSymNode); ok { 600 // Stat doesn't work for symlinks, so use lookup 601 n := node.parentDir.(libkbfs.Node) 602 _, info, err = kbfsOps.Lookup(ctx, n, n.ChildName(node.name)) 603 } 604 if err != nil { 605 return nil, err 606 } 607 return info.PrevRevisions, nil 608 } 609 610 // getRootNode is like GetRootDir, but doesn't check the canonical TLF 611 // name. 612 func getRootNode(ctx context.Context, config libkbfs.Config, tlfName string, 613 t tlf.Type) (libkbfs.Node, error) { 614 h, err := parseTlfHandle( 615 ctx, config.KBPKI(), config.MDOps(), config, tlfName, t) 616 if err != nil { 617 return nil, err 618 } 619 620 // TODO: we should cache the root node, to more faithfully 621 // simulate real-world callers and avoid unnecessary work. 622 kbfsOps := config.KBFSOps() 623 if h.IsLocalConflict() { 624 b, ok := data.MakeConflictBranchName(h) 625 if ok { 626 dir, _, err := kbfsOps.GetRootNode(ctx, h, b) 627 if err != nil { 628 return nil, err 629 } 630 return dir, nil 631 } 632 } 633 634 dir, _, err := kbfsOps.GetOrCreateRootNode(ctx, h, data.MasterBranch) 635 if err != nil { 636 return nil, err 637 } 638 return dir, nil 639 } 640 641 // DisableUpdatesForTesting implements the Engine interface. 642 func (k *LibKBFS) DisableUpdatesForTesting(u User, tlfName string, t tlf.Type) (err error) { 643 config := u.(*libkbfs.ConfigLocal) 644 645 ctx, cancel := k.newContext(u) 646 defer cancel() 647 dir, err := getRootNode(ctx, config, tlfName, t) 648 if err != nil { 649 return err 650 } 651 652 if _, ok := k.updateChannels[config][dir.GetFolderBranch()]; ok { 653 // Updates are already disabled. 654 return nil 655 } 656 657 var c chan<- struct{} 658 c, err = libkbfs.DisableUpdatesForTesting(config, dir.GetFolderBranch()) 659 if err != nil { 660 return err 661 } 662 k.updateChannels[config][dir.GetFolderBranch()] = c 663 // Also stop conflict resolution. 664 err = libkbfs.DisableCRForTesting(config, dir.GetFolderBranch()) 665 if err != nil { 666 return err 667 } 668 return nil 669 } 670 671 // MakeNaïveStaller implements the Engine interface. 672 func (*LibKBFS) MakeNaïveStaller(u User) *libkbfs.NaïveStaller { 673 return libkbfs.NewNaïveStaller(u.(*libkbfs.ConfigLocal)) 674 } 675 676 // ReenableUpdates implements the Engine interface. 677 func (k *LibKBFS) ReenableUpdates(u User, tlfName string, t tlf.Type) error { 678 config := u.(*libkbfs.ConfigLocal) 679 680 ctx, cancel := k.newContext(u) 681 defer cancel() 682 dir, err := getRootNode(ctx, config, tlfName, t) 683 if err != nil { 684 return err 685 } 686 687 c, ok := k.updateChannels[config][dir.GetFolderBranch()] 688 if !ok { 689 return fmt.Errorf( 690 "Couldn't re-enable updates for %s (type=%s)", tlfName, t) 691 } 692 693 // Restart CR using a clean context, since we will cancel ctx when 694 // we return. 695 err = libkbfs.RestartCRForTesting( 696 libcontext.BackgroundContextWithCancellationDelayer(), config, 697 dir.GetFolderBranch()) 698 if err != nil { 699 return err 700 } 701 702 c <- struct{}{} 703 close(c) 704 delete(k.updateChannels[config], dir.GetFolderBranch()) 705 return nil 706 } 707 708 // SyncFromServer implements the Engine interface. 709 func (k *LibKBFS) SyncFromServer(u User, tlfName string, t tlf.Type) (err error) { 710 config := u.(*libkbfs.ConfigLocal) 711 712 ctx, cancel := k.newContext(u) 713 defer cancel() 714 dir, err := getRootNode(ctx, config, tlfName, t) 715 if err != nil { 716 return err 717 } 718 719 return config.KBFSOps().SyncFromServer(ctx, 720 dir.GetFolderBranch(), nil) 721 } 722 723 // ForceQuotaReclamation implements the Engine interface. 724 func (k *LibKBFS) ForceQuotaReclamation(u User, tlfName string, t tlf.Type) (err error) { 725 config := u.(*libkbfs.ConfigLocal) 726 727 ctx, cancel := k.newContext(u) 728 defer cancel() 729 dir, err := getRootNode(ctx, config, tlfName, t) 730 if err != nil { 731 return err 732 } 733 734 return libkbfs.ForceQuotaReclamationForTesting( 735 config, dir.GetFolderBranch()) 736 } 737 738 // AddNewAssertion implements the Engine interface. 739 func (k *LibKBFS) AddNewAssertion(u User, oldAssertion, newAssertion string) error { 740 config := u.(*libkbfs.ConfigLocal) 741 return libkbfs.AddNewAssertionForTest(config, oldAssertion, newAssertion) 742 } 743 744 // ChangeTeamName implements the Engine interface. 745 func (k *LibKBFS) ChangeTeamName(u User, oldName, newName string) error { 746 config := u.(*libkbfs.ConfigLocal) 747 return libkbfs.ChangeTeamNameForTest(config, oldName, newName) 748 } 749 750 // Rekey implements the Engine interface. 751 func (k *LibKBFS) Rekey(u User, tlfName string, t tlf.Type) error { 752 config := u.(*libkbfs.ConfigLocal) 753 754 ctx, cancel := k.newContext(u) 755 defer cancel() 756 dir, err := getRootNode(ctx, config, tlfName, t) 757 if err != nil { 758 return err 759 } 760 761 _, err = libkbfs.RequestRekeyAndWaitForOneFinishEvent(ctx, 762 config.KBFSOps(), dir.GetFolderBranch().Tlf) 763 return err 764 } 765 766 // EnableJournal implements the Engine interface. 767 func (k *LibKBFS) EnableJournal(u User, tlfName string, t tlf.Type) error { 768 config := u.(*libkbfs.ConfigLocal) 769 770 ctx, cancel := k.newContext(u) 771 defer cancel() 772 dir, err := getRootNode(ctx, config, tlfName, t) 773 if err != nil { 774 return err 775 } 776 777 jManager, err := libkbfs.GetJournalManager(config) 778 if err != nil { 779 return err 780 } 781 782 h, err := parseTlfHandle( 783 ctx, config.KBPKI(), config.MDOps(), config, tlfName, t) 784 if err != nil { 785 return err 786 } 787 788 return jManager.Enable(ctx, dir.GetFolderBranch().Tlf, h, 789 libkbfs.TLFJournalBackgroundWorkEnabled) 790 } 791 792 // PauseJournal implements the Engine interface. 793 func (k *LibKBFS) PauseJournal(u User, tlfName string, t tlf.Type) error { 794 config := u.(*libkbfs.ConfigLocal) 795 796 ctx, cancel := k.newContext(u) 797 defer cancel() 798 dir, err := getRootNode(ctx, config, tlfName, t) 799 if err != nil { 800 return err 801 } 802 803 jManager, err := libkbfs.GetJournalManager(config) 804 if err != nil { 805 return err 806 } 807 808 jManager.PauseBackgroundWork(ctx, dir.GetFolderBranch().Tlf) 809 return nil 810 } 811 812 // ResumeJournal implements the Engine interface. 813 func (k *LibKBFS) ResumeJournal(u User, tlfName string, t tlf.Type) error { 814 config := u.(*libkbfs.ConfigLocal) 815 816 ctx, cancel := k.newContext(u) 817 defer cancel() 818 dir, err := getRootNode(ctx, config, tlfName, t) 819 if err != nil { 820 return err 821 } 822 823 jManager, err := libkbfs.GetJournalManager(config) 824 if err != nil { 825 return err 826 } 827 828 jManager.ResumeBackgroundWork(ctx, dir.GetFolderBranch().Tlf) 829 return nil 830 } 831 832 // FlushJournal implements the Engine interface. 833 func (k *LibKBFS) FlushJournal(u User, tlfName string, t tlf.Type) error { 834 config := u.(*libkbfs.ConfigLocal) 835 836 ctx, cancel := k.newContext(u) 837 defer cancel() 838 dir, err := getRootNode(ctx, config, tlfName, t) 839 if err != nil { 840 return err 841 } 842 843 jManager, err := libkbfs.GetJournalManager(config) 844 if err != nil { 845 return err 846 } 847 848 return jManager.Flush(ctx, dir.GetFolderBranch().Tlf) 849 } 850 851 // UnflushedPaths implements the Engine interface. 852 func (k *LibKBFS) UnflushedPaths(u User, tlfName string, t tlf.Type) ( 853 []string, error) { 854 config := u.(*libkbfs.ConfigLocal) 855 856 ctx, cancel := k.newContext(u) 857 defer cancel() 858 dir, err := getRootNode(ctx, config, tlfName, t) 859 if err != nil { 860 return nil, err 861 } 862 863 status, _, err := config.KBFSOps().FolderStatus(ctx, dir.GetFolderBranch()) 864 if err != nil { 865 return nil, err 866 } 867 868 return status.Journal.UnflushedPaths, nil 869 } 870 871 // UserEditHistory implements the Engine interface. 872 func (k *LibKBFS) UserEditHistory(u User) ( 873 []keybase1.FSFolderEditHistory, error) { 874 config := u.(*libkbfs.ConfigLocal) 875 876 ctx, cancel := k.newContext(u) 877 defer cancel() 878 session, err := idutil.GetCurrentSessionIfPossible( 879 ctx, config.KBPKI(), true) 880 if err != nil { 881 return nil, err 882 } 883 884 history := config.UserHistory().Get(string(session.Name)) 885 return history, nil 886 } 887 888 // DirtyPaths implements the Engine interface. 889 func (k *LibKBFS) DirtyPaths(u User, tlfName string, t tlf.Type) ( 890 []string, error) { 891 config := u.(*libkbfs.ConfigLocal) 892 893 ctx, cancel := k.newContext(u) 894 defer cancel() 895 dir, err := getRootNode(ctx, config, tlfName, t) 896 if err != nil { 897 return nil, err 898 } 899 900 status, _, err := config.KBFSOps().FolderStatus(ctx, dir.GetFolderBranch()) 901 if err != nil { 902 return nil, err 903 } 904 905 return status.DirtyPaths, nil 906 } 907 908 // TogglePrefetch implements the Engine interface. 909 func (k *LibKBFS) TogglePrefetch(u User, enable bool) error { 910 config := u.(*libkbfs.ConfigLocal) 911 912 _ = config.BlockOps().TogglePrefetcher(enable) 913 return nil 914 } 915 916 // ForceConflict implements the Engine interface. 917 func (k *LibKBFS) ForceConflict(u User, tlfName string, t tlf.Type) error { 918 config := u.(*libkbfs.ConfigLocal) 919 920 ctx, cancel := k.newContext(u) 921 defer cancel() 922 923 root, err := getRootNode(ctx, config, tlfName, t) 924 if err != nil { 925 return err 926 } 927 928 return config.KBFSOps().ForceStuckConflictForTesting( 929 ctx, root.GetFolderBranch().Tlf) 930 } 931 932 // ClearConflicts implements the Engine interface. 933 func (k *LibKBFS) ClearConflicts(u User, tlfName string, t tlf.Type) error { 934 config := u.(*libkbfs.ConfigLocal) 935 936 ctx, cancel := k.newContext(u) 937 defer cancel() 938 939 root, err := getRootNode(ctx, config, tlfName, t) 940 if err != nil { 941 return err 942 } 943 944 return config.KBFSOps().ClearConflictView(ctx, root.GetFolderBranch().Tlf) 945 } 946 947 // Shutdown implements the Engine interface. 948 func (k *LibKBFS) Shutdown(u User) error { 949 config := u.(*libkbfs.ConfigLocal) 950 // drop references 951 k.refs[config] = make(map[libkbfs.Node]bool) 952 delete(k.refs, config) 953 // clear update channels 954 k.updateChannels[config] = make(map[data.FolderBranch]chan<- struct{}) 955 delete(k.updateChannels, config) 956 957 // Get the user name before shutting everything down. 958 var userName kbname.NormalizedUsername 959 if k.journalDir != "" { 960 var err error 961 session, err := 962 config.KBPKI().GetCurrentSession(context.Background()) 963 if err != nil { 964 return err 965 } 966 userName = session.Name 967 } 968 969 // shutdown 970 ctx := context.Background() 971 if err := config.Shutdown(ctx); err != nil { 972 return err 973 } 974 975 if k.journalDir != "" { 976 // Remove the user journal. 977 if err := ioutil.RemoveAll( 978 filepath.Join(k.journalDir, userName.String())); err != nil { 979 return err 980 } 981 // Remove the overall journal dir if it's empty. 982 if err := ioutil.Remove(k.journalDir); err != nil { 983 k.tb.Logf("Journal dir %s not empty yet", k.journalDir) 984 } 985 } 986 return nil 987 }