github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/conflict_resolver.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 "encoding/json" 9 "fmt" 10 "os" 11 sysPath "path" 12 "runtime/debug" 13 "sort" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/keybase/client/go/kbfs/data" 19 "github.com/keybase/client/go/kbfs/idutil" 20 "github.com/keybase/client/go/kbfs/kbfsblock" 21 "github.com/keybase/client/go/kbfs/kbfscrypto" 22 "github.com/keybase/client/go/kbfs/kbfsmd" 23 "github.com/keybase/client/go/kbfs/kbfssync" 24 "github.com/keybase/client/go/kbfs/ldbutils" 25 "github.com/keybase/client/go/kbfs/libcontext" 26 "github.com/keybase/client/go/protocol/keybase1" 27 "github.com/keybase/go-codec/codec" 28 "github.com/pkg/errors" 29 "github.com/syndtr/goleveldb/leveldb" 30 "github.com/syndtr/goleveldb/leveldb/storage" 31 "golang.org/x/net/context" 32 ) 33 34 // CtxCRTagKey is the type used for unique context tags related to 35 // conflict resolution 36 type CtxCRTagKey int 37 38 type failModeForTesting int 39 40 const ( 41 // CtxCRIDKey is the type of the tag for unique operation IDs 42 // related to conflict resolution 43 CtxCRIDKey CtxCRTagKey = iota 44 45 // If the number of outstanding unmerged revisions that need to be 46 // resolved together is greater than this number, then block 47 // unmerged writes to make sure we don't get *too* unmerged. 48 // TODO: throttle unmerged writes before resorting to complete 49 // blockage. 50 crMaxRevsThresholdDefault = 500 51 52 // How long we're allowed to block writes for if we exceed the max 53 // revisions threshold. 54 crMaxWriteLockTime = 10 * time.Second 55 56 // Where in config.StorageRoot() we store information about failed conflict 57 // resolutions. 58 conflictResolverRecordsDir = "kbfs_conflicts" 59 conflictResolverRecordsVersionString = "v1" 60 conflictResolverRecordsDB = "kbfsConflicts.leveldb" 61 62 // If we have failed at CR 10 times, probably it's never going to work and 63 // we should give up. 64 maxConflictResolutionAttempts = 10 65 66 alwaysFailCR failModeForTesting = iota 67 doNotAlwaysFailCR 68 ) 69 70 // ErrTooManyCRAttempts is an error that indicates that CR has failed 71 // too many times, and it being stopped. 72 var ErrTooManyCRAttempts = errors.New( 73 "too many attempts at conflict resolution on this TLF") 74 75 // ErrCRFailForTesting indicates that CR is disabled for a test. 76 var ErrCRFailForTesting = errors.New( 77 "conflict resolution failed because test requested it") 78 79 // CtxCROpID is the display name for the unique operation 80 // conflict resolution ID tag. 81 const CtxCROpID = "CRID" 82 83 type conflictInput struct { 84 unmerged kbfsmd.Revision 85 merged kbfsmd.Revision 86 } 87 88 var errNoCRDB = errors.New("could not record CR attempt because no DB is open") 89 90 // ConflictResolver is responsible for resolving conflicts in the 91 // background. 92 type ConflictResolver struct { 93 config Config 94 fbo *folderBranchOps 95 prepper folderUpdatePrepper 96 log traceLogger 97 deferLog traceLogger 98 maxRevsThreshold int 99 100 inputChanLock sync.RWMutex 101 inputChan chan conflictInput 102 103 // resolveGroup tracks the outstanding resolves. 104 resolveGroup kbfssync.RepeatedWaitGroup 105 106 inputLock sync.Mutex 107 currInput conflictInput 108 currCancel context.CancelFunc 109 lockNextTime bool 110 canceledCount int 111 112 failModeLock sync.RWMutex 113 failModeForTesting failModeForTesting 114 } 115 116 // NewConflictResolver constructs a new ConflictResolver (and launches 117 // any necessary background goroutines). 118 func NewConflictResolver( 119 config Config, fbo *folderBranchOps) *ConflictResolver { 120 // make a logger with an appropriate module name 121 branchSuffix := "" 122 if fbo.branch() != data.MasterBranch { 123 branchSuffix = " " + string(fbo.branch()) 124 } 125 tlfStringFull := fbo.id().String() 126 log := config.MakeLogger( 127 fmt.Sprintf("CR %s%s", tlfStringFull[:8], branchSuffix)) 128 129 cr := &ConflictResolver{ 130 config: config, 131 fbo: fbo, 132 prepper: folderUpdatePrepper{ 133 config: config, 134 folderBranch: fbo.folderBranch, 135 blocks: &fbo.blocks, 136 log: log, 137 vlog: config.MakeVLogger(log), 138 }, 139 log: traceLogger{log}, 140 deferLog: traceLogger{log.CloneWithAddedDepth(1)}, 141 maxRevsThreshold: crMaxRevsThresholdDefault, 142 currInput: conflictInput{ 143 unmerged: kbfsmd.RevisionUninitialized, 144 merged: kbfsmd.RevisionUninitialized, 145 }, 146 } 147 148 if fbo.bType == standard && config.Mode().ConflictResolutionEnabled() { 149 cr.startProcessing(libcontext.BackgroundContextWithCancellationDelayer()) 150 } 151 return cr 152 } 153 154 func (cr *ConflictResolver) startProcessing(baseCtx context.Context) { 155 cr.inputChanLock.Lock() 156 defer cr.inputChanLock.Unlock() 157 158 if cr.inputChan != nil { 159 return 160 } 161 cr.inputChan = make(chan conflictInput) 162 go cr.processInput(baseCtx, cr.inputChan) 163 } 164 165 func (cr *ConflictResolver) stopProcessing() { 166 cr.inputChanLock.Lock() 167 defer cr.inputChanLock.Unlock() 168 169 if cr.inputChan == nil { 170 return 171 } 172 close(cr.inputChan) 173 cr.inputChan = nil 174 } 175 176 // cancelExistingLocked must be called while holding cr.inputLock. 177 func (cr *ConflictResolver) cancelExistingLocked(ci conflictInput) bool { 178 // The input is only interesting if one of the revisions is 179 // greater than what we've looked at to date. 180 if ci.unmerged <= cr.currInput.unmerged && 181 ci.merged <= cr.currInput.merged { 182 return false 183 } 184 if cr.currCancel != nil { 185 cr.currCancel() 186 } 187 return true 188 } 189 190 // ForceCancel cancels any currently-running CR, regardless of what 191 // its inputs were. 192 func (cr *ConflictResolver) ForceCancel() { 193 cr.inputLock.Lock() 194 defer cr.inputLock.Unlock() 195 if cr.currCancel != nil { 196 cr.currCancel() 197 } 198 } 199 200 // processInput processes conflict resolution jobs from the given 201 // channel until it is closed. This function uses a parameter for the 202 // channel instead of accessing cr.inputChan directly so that it 203 // doesn't have to hold inputChanLock. 204 func (cr *ConflictResolver) processInput(baseCtx context.Context, 205 inputChan <-chan conflictInput) { 206 207 // Start off with a closed prevCRDone, so that the first CR call 208 // doesn't have to wait. 209 prevCRDone := make(chan struct{}) 210 close(prevCRDone) 211 defer func() { 212 cr.inputLock.Lock() 213 defer cr.inputLock.Unlock() 214 if cr.currCancel != nil { 215 cr.currCancel() 216 } 217 _ = libcontext.CleanupCancellationDelayer(baseCtx) 218 }() 219 for ci := range inputChan { 220 ctx := CtxWithRandomIDReplayable(baseCtx, CtxCRIDKey, CtxCROpID, cr.log) 221 222 valid := func() bool { 223 cr.inputLock.Lock() 224 defer cr.inputLock.Unlock() 225 valid := cr.cancelExistingLocked(ci) 226 if !valid { 227 return false 228 } 229 cr.log.CDebugf(ctx, "New conflict input %v following old "+ 230 "input %v", ci, cr.currInput) 231 cr.currInput = ci 232 ctx, cr.currCancel = context.WithCancel(ctx) 233 return true 234 }() 235 if !valid { 236 cr.log.CDebugf(ctx, "Ignoring uninteresting input: %v", ci) 237 cr.resolveGroup.Done() 238 continue 239 } 240 241 waitChan := prevCRDone 242 prevCRDone = make(chan struct{}) // closed when doResolve finishes 243 go func(ci conflictInput, done chan<- struct{}) { 244 defer cr.resolveGroup.Done() 245 defer close(done) 246 // Wait for the previous CR without blocking any 247 // Resolve callers, as that could result in deadlock 248 // (KBFS-1001). 249 select { 250 case <-waitChan: 251 case <-ctx.Done(): 252 cr.log.CDebugf(ctx, "Resolution canceled before starting") 253 // The next attempt will still need to wait on the old 254 // one, in case it hasn't finished yet. So wait for 255 // it here, before we close our own `done` channel. 256 <-waitChan 257 return 258 } 259 cr.doResolve(ctx, ci) 260 }(ci, prevCRDone) 261 } 262 } 263 264 // Resolve takes the latest known unmerged and merged revision 265 // numbers, and kicks off the resolution process. 266 func (cr *ConflictResolver) Resolve(ctx context.Context, 267 unmerged kbfsmd.Revision, merged kbfsmd.Revision) { 268 cr.inputChanLock.RLock() 269 defer cr.inputChanLock.RUnlock() 270 271 // CR can end up trying to cancel itself via the SyncAll call, so 272 // prevent that from happening. 273 if crOpID := ctx.Value(CtxCRIDKey); crOpID != nil { 274 cr.log.CDebugf(ctx, "Ignoring self-resolve during CR") 275 return 276 } 277 278 if cr.inputChan == nil { 279 return 280 } 281 282 // Call Add before cancelling existing CR in order to prevent the 283 // resolveGroup from becoming briefly empty and allowing things waiting 284 // on it to believe that CR has finished. 285 cr.resolveGroup.Add(1) 286 287 ci := conflictInput{unmerged, merged} 288 func() { 289 cr.inputLock.Lock() 290 defer cr.inputLock.Unlock() 291 // Cancel any running CR before we return, so the caller can be 292 // confident any ongoing CR superseded by this new input will be 293 // canceled before it releases any locks it holds. 294 // 295 // TODO: return early if this returns false, and log something 296 // using a newly-pass-in context. 297 _ = cr.cancelExistingLocked(ci) 298 }() 299 300 cr.inputChan <- ci 301 } 302 303 // Wait blocks until the current set of submitted resolutions are 304 // complete (though not necessarily successful), or until the given 305 // context is canceled. 306 func (cr *ConflictResolver) Wait(ctx context.Context) error { 307 return cr.resolveGroup.Wait(ctx) 308 } 309 310 // Shutdown cancels any ongoing resolutions and stops any background 311 // goroutines. 312 func (cr *ConflictResolver) Shutdown() { 313 cr.stopProcessing() 314 } 315 316 // Pause cancels any ongoing resolutions and prevents any new ones from 317 // starting. 318 func (cr *ConflictResolver) Pause() { 319 cr.stopProcessing() 320 } 321 322 // Restart re-enables conflict resolution, with a base context for CR 323 // operations. baseCtx must have a cancellation delayer. 324 func (cr *ConflictResolver) Restart(baseCtx context.Context) { 325 cr.startProcessing(baseCtx) 326 } 327 328 // BeginNewBranch resets any internal state to be ready to accept 329 // resolutions from a new branch. 330 func (cr *ConflictResolver) BeginNewBranch() { 331 cr.inputLock.Lock() 332 defer cr.inputLock.Unlock() 333 // Reset the curr input so we don't ignore a future CR 334 // request that uses the same revision number (i.e., 335 // because the previous CR failed to flush due to a 336 // conflict). 337 cr.currInput = conflictInput{} 338 } 339 340 func (cr *ConflictResolver) checkDone(ctx context.Context) error { 341 select { 342 case <-ctx.Done(): 343 return ctx.Err() 344 default: 345 return nil 346 } 347 } 348 349 func (cr *ConflictResolver) getMDs( 350 ctx context.Context, lState *kbfssync.LockState, 351 writerLocked bool) (unmerged []ImmutableRootMetadata, 352 merged []ImmutableRootMetadata, err error) { 353 // First get all outstanding unmerged MDs for this device. 354 var branchPoint kbfsmd.Revision 355 if writerLocked { 356 branchPoint, unmerged, err = 357 cr.fbo.getUnmergedMDUpdatesLocked(ctx, lState) 358 } else { 359 branchPoint, unmerged, err = 360 cr.fbo.getUnmergedMDUpdates(ctx, lState) 361 } 362 if err != nil { 363 return nil, nil, err 364 } 365 366 for i, md := range unmerged { 367 newMd, err := reembedBlockChangesIntoCopyIfNeeded( 368 ctx, cr.config.Codec(), cr.config.BlockCache(), 369 cr.config.BlockOps(), cr.config.Mode(), md, cr.log) 370 if err != nil { 371 return nil, nil, err 372 } 373 unmerged[i] = newMd 374 } 375 376 if len(unmerged) > 0 && unmerged[0].BID() == kbfsmd.PendingLocalSquashBranchID { 377 cr.log.CDebugf(ctx, "Squashing local branch") 378 return unmerged, nil, nil 379 } 380 381 // Now get all the merged MDs, starting from after the branch 382 // point. We fetch the branch point (if possible) to make sure 383 // it's the right predecessor of the unmerged branch. TODO: stop 384 // fetching the branch point and remove the successor check below 385 // once we fix KBFS-1664. 386 fetchFrom := branchPoint + 1 387 if branchPoint >= kbfsmd.RevisionInitial { 388 fetchFrom = branchPoint 389 } 390 merged, err = getMergedMDUpdates( 391 ctx, cr.fbo.config, cr.fbo.id(), fetchFrom, nil) 392 if err != nil { 393 return nil, nil, err 394 } 395 396 if len(unmerged) > 0 { 397 err := merged[0].CheckValidSuccessor( 398 merged[0].mdID, unmerged[0].ReadOnly()) 399 if err != nil { 400 cr.log.CDebugf(ctx, "Branch point (rev=%d, mdID=%s) is not a "+ 401 "valid successor for unmerged rev %d (mdID=%s, bid=%s)", 402 merged[0].Revision(), merged[0].mdID, unmerged[0].Revision(), 403 unmerged[0].mdID, unmerged[0].BID()) 404 return nil, nil, err 405 } 406 } 407 408 // Remove branch point. 409 if len(merged) > 0 && fetchFrom == branchPoint { 410 merged = merged[1:] 411 } 412 413 return unmerged, merged, nil 414 } 415 416 // updateCurrInput assumes that both unmerged and merged are 417 // non-empty. 418 func (cr *ConflictResolver) updateCurrInput(ctx context.Context, 419 unmerged, merged []ImmutableRootMetadata) (err error) { 420 cr.inputLock.Lock() 421 defer cr.inputLock.Unlock() 422 // check done while holding the lock, so we know for sure if 423 // we've already been canceled and replaced by a new input. 424 err = cr.checkDone(ctx) 425 if err != nil { 426 return err 427 } 428 429 prevInput := cr.currInput 430 defer func() { 431 // reset the currInput if we get an error below 432 if err != nil { 433 cr.currInput = prevInput 434 } 435 }() 436 437 rev := unmerged[len(unmerged)-1].bareMd.RevisionNumber() 438 if rev < cr.currInput.unmerged { 439 return fmt.Errorf("Unmerged revision %d is lower than the "+ 440 "expected unmerged revision %d", rev, cr.currInput.unmerged) 441 } 442 cr.currInput.unmerged = rev 443 444 if len(merged) > 0 { 445 rev = merged[len(merged)-1].bareMd.RevisionNumber() 446 if rev < cr.currInput.merged { 447 return fmt.Errorf("Merged revision %d is lower than the "+ 448 "expected merged revision %d", rev, cr.currInput.merged) 449 } 450 } else { 451 rev = kbfsmd.RevisionUninitialized 452 } 453 cr.currInput.merged = rev 454 455 // Take the lock right away next time if either there are lots of 456 // unmerged revisions, or this is a local squash and we won't 457 // block for very long. 458 // 459 // TODO: if there are a lot of merged revisions, and they keep 460 // coming, we might consider doing a "partial" resolution, writing 461 // the result back to the unmerged branch (basically "rebasing" 462 // it). See KBFS-1896. 463 if (len(unmerged) > cr.maxRevsThreshold) || 464 (len(unmerged) > 0 && unmerged[0].BID() == kbfsmd.PendingLocalSquashBranchID) { 465 cr.lockNextTime = true 466 } 467 return nil 468 } 469 470 func (cr *ConflictResolver) makeChains(ctx context.Context, 471 unmerged, merged []ImmutableRootMetadata) ( 472 unmergedChains, mergedChains *crChains, err error) { 473 unmergedChains, err = newCRChainsForIRMDs( 474 ctx, cr.config.Codec(), cr.config, unmerged, &cr.fbo.blocks, true) 475 if err != nil { 476 return nil, nil, err 477 } 478 479 // Make sure we don't try to unref any blocks that have already 480 // been GC'd in the merged branch. 481 for _, md := range merged { 482 for _, op := range md.data.Changes.Ops { 483 _, isGCOp := op.(*GCOp) 484 if !isGCOp { 485 continue 486 } 487 for _, ptr := range op.Unrefs() { 488 unmergedChains.doNotUnrefPointers[ptr] = true 489 } 490 } 491 } 492 493 // If there are no new merged changes, don't make any merged 494 // chains. 495 if len(merged) == 0 { 496 return unmergedChains, newCRChainsEmpty(nil), nil 497 } 498 499 mergedChains, err = newCRChainsForIRMDs( 500 ctx, cr.config.Codec(), cr.config, merged, &cr.fbo.blocks, true) 501 if err != nil { 502 return nil, nil, err 503 } 504 505 // Make the chain summaries. Identify using the unmerged chains, 506 // since those are most likely to be able to identify a node in 507 // the cache. 508 unmergedSummary := unmergedChains.summary(unmergedChains, cr.fbo.nodeCache) 509 mergedSummary := mergedChains.summary(unmergedChains, cr.fbo.nodeCache) 510 511 // Ignore CR summaries for pending local squashes. 512 if len(unmerged) == 0 || unmerged[0].BID() != kbfsmd.PendingLocalSquashBranchID { 513 cr.fbo.status.setCRSummary(unmergedSummary, mergedSummary) 514 } 515 return unmergedChains, mergedChains, nil 516 } 517 518 // A helper class that implements sort.Interface to sort paths by 519 // descending path length. 520 type crSortedPaths []data.Path 521 522 // Len implements sort.Interface for crSortedPaths 523 func (sp crSortedPaths) Len() int { 524 return len(sp) 525 } 526 527 // Less implements sort.Interface for crSortedPaths 528 func (sp crSortedPaths) Less(i, j int) bool { 529 return len(sp[i].Path) > len(sp[j].Path) 530 } 531 532 // Swap implements sort.Interface for crSortedPaths 533 func (sp crSortedPaths) Swap(i, j int) { 534 sp[j], sp[i] = sp[i], sp[j] 535 } 536 537 func createdFileWithConflictingWrite(unmergedChains, mergedChains *crChains, 538 unmergedOriginal, mergedOriginal data.BlockPointer) bool { 539 mergedChain := mergedChains.byOriginal[mergedOriginal] 540 unmergedChain := unmergedChains.byOriginal[unmergedOriginal] 541 if mergedChain == nil || unmergedChain == nil { 542 return false 543 } 544 545 unmergedWriteRange := unmergedChain.getCollapsedWriteRange() 546 mergedWriteRange := mergedChain.getCollapsedWriteRange() 547 // Are they exactly equivalent? 548 if writeRangesEquivalent(unmergedWriteRange, mergedWriteRange) { 549 unmergedChain.removeSyncOps() 550 return false 551 } 552 553 // If the unmerged one is just a truncate, we can safely ignore 554 // the unmerged chain. 555 if len(unmergedWriteRange) == 1 && unmergedWriteRange[0].isTruncate() && 556 unmergedWriteRange[0].Off == 0 { 557 unmergedChain.removeSyncOps() 558 return false 559 } 560 561 // If the merged one is just a truncate, we can safely ignore 562 // the merged chain. 563 if len(mergedWriteRange) == 1 && mergedWriteRange[0].isTruncate() && 564 mergedWriteRange[0].Off == 0 { 565 mergedChain.removeSyncOps() 566 return false 567 } 568 569 return true 570 } 571 572 // createdFileWithNonzeroSizes checks two possibly-conflicting 573 // createOps and returns true if the corresponding file has non-zero 574 // directory entry sizes in both the unmerged and merged branch. We 575 // need to check this sometimes, because a call to 576 // `createdFileWithConflictingWrite` might not have access to syncOps 577 // that have been resolved away in a previous iteration. See 578 // KBFS-2825 for details. 579 func (cr *ConflictResolver) createdFileWithNonzeroSizes( 580 ctx context.Context, unmergedChains, mergedChains *crChains, 581 unmergedChain *crChain, mergedChain *crChain, 582 unmergedCop, mergedCop *createOp) (bool, error) { 583 lState := makeFBOLockState() 584 // The pointers on the ops' final paths aren't necessarily filled 585 // in, so construct our own partial paths using the chain 586 // pointers, which are enough to satisfy `GetEntry`. 587 mergedPath := data.Path{ 588 FolderBranch: mergedCop.getFinalPath().FolderBranch, 589 Path: []data.PathNode{ 590 {BlockPointer: mergedChain.mostRecent, 591 Name: data.NewPathPartString("", nil)}, 592 {BlockPointer: data.ZeroPtr, Name: mergedCop.obfuscatedNewName()}, 593 }, 594 } 595 kmd := mergedChains.mostRecentChainMDInfo 596 mergedEntry, err := cr.fbo.blocks.GetEntry(ctx, lState, kmd, mergedPath) 597 if _, noExists := errors.Cause(err).(idutil.NoSuchNameError); noExists { 598 return false, nil 599 } else if err != nil { 600 return false, err 601 } 602 603 kmd = unmergedChains.mostRecentChainMDInfo 604 unmergedPath := data.Path{ 605 FolderBranch: mergedCop.getFinalPath().FolderBranch, 606 Path: []data.PathNode{ 607 {BlockPointer: unmergedChain.mostRecent, 608 Name: data.NewPathPartString("", nil)}, 609 {BlockPointer: data.ZeroPtr, Name: mergedCop.obfuscatedNewName()}, 610 }, 611 } 612 unmergedEntry, err := cr.fbo.blocks.GetEntry(ctx, lState, kmd, unmergedPath) 613 if _, noExists := errors.Cause(err).(idutil.NoSuchNameError); noExists { 614 return false, nil 615 } else if err != nil { 616 return false, err 617 } 618 619 if mergedEntry.Size > 0 && unmergedEntry.Size > 0 { 620 cr.log.CDebugf(ctx, 621 "Not merging files named %s with non-zero sizes "+ 622 "(merged=%d unmerged=%d)", 623 unmergedCop.NewName, mergedEntry.Size, unmergedEntry.Size) 624 return true, nil 625 } 626 return false, nil 627 } 628 629 // checkPathForMerge checks whether the given unmerged chain and path 630 // contains any newly-created subdirectories that were created 631 // simultaneously in the merged branch as well. If so, it recursively 632 // checks that directory as well. It returns a slice of any new 633 // unmerged paths that need to be checked for conflicts later in 634 // conflict resolution, for all subdirectories of the given path. 635 func (cr *ConflictResolver) checkPathForMerge(ctx context.Context, 636 unmergedChain *crChain, unmergedPath data.Path, 637 unmergedChains, mergedChains *crChains) ([]data.Path, error) { 638 mergedChain, ok := mergedChains.byOriginal[unmergedChain.original] 639 if !ok { 640 // No corresponding merged chain means we don't have to merge 641 // any directories. 642 return nil, nil 643 } 644 645 // Find instances of the same directory being created in both 646 // branches. Only merge completely new directories -- anything 647 // involving a rename will result in a conflict for now. 648 // 649 // TODO: have a better merge strategy for renamed directories! 650 mergedCreates := make(map[string]*createOp) 651 for _, op := range mergedChain.ops { 652 cop, ok := op.(*createOp) 653 if !ok || len(cop.Refs()) == 0 || cop.renamed { 654 continue 655 } 656 mergedCreates[cop.NewName] = cop 657 } 658 659 if len(mergedCreates) == 0 { 660 return nil, nil 661 } 662 663 var newUnmergedPaths []data.Path 664 toDrop := make(map[int]bool) 665 for i, op := range unmergedChain.ops { 666 cop, ok := op.(*createOp) 667 if !ok || len(cop.Refs()) == 0 || cop.renamed { 668 continue 669 } 670 671 // Is there a corresponding merged create with the same type? 672 mergedCop, ok := mergedCreates[cop.NewName] 673 if !ok || mergedCop.Type != cop.Type { 674 continue 675 } 676 unmergedOriginal := cop.Refs()[0] 677 mergedOriginal := mergedCop.Refs()[0] 678 if cop.Type != data.Dir { 679 // Only merge files if they don't both have writes. 680 // Double-check the directory blocks to see if the files 681 // have non-zero sizes, because an earlier resolution 682 // might have collapsed all the sync ops away. 683 if createdFileWithConflictingWrite(unmergedChains, mergedChains, 684 unmergedOriginal, mergedOriginal) { 685 continue 686 } 687 conflicts, err := cr.createdFileWithNonzeroSizes( 688 ctx, unmergedChains, mergedChains, unmergedChain, mergedChain, 689 cop, mergedCop) 690 if err != nil { 691 return nil, err 692 } 693 if conflicts { 694 continue 695 } 696 } 697 698 toDrop[i] = true 699 700 cr.log.CDebugf(ctx, "Merging name %s (%s) in %v (unmerged original %v "+ 701 "changed to %v)", cop.NewName, cop.Type, unmergedChain.mostRecent, 702 unmergedOriginal, mergedOriginal) 703 // Change the original to match the merged original, so we can 704 // check for conflicts later. Note that the most recent will 705 // stay the same, so we can still match the unmerged path 706 // correctly. 707 err := unmergedChains.changeOriginal(unmergedOriginal, mergedOriginal) 708 if _, notFound := errors.Cause(err).(NoChainFoundError); notFound { 709 unmergedChains.toUnrefPointers[unmergedOriginal] = true 710 continue 711 } 712 if err != nil { 713 return nil, err 714 } else if unmergedOriginal == mergedOriginal { 715 cr.log.CDebugf(ctx, 716 "Treating self-conflicting directory like a normal conflict") 717 } 718 719 unmergedChain, ok := unmergedChains.byOriginal[mergedOriginal] 720 if !ok { 721 return nil, fmt.Errorf("Change original (%v -> %v) didn't work", 722 unmergedOriginal, mergedOriginal) 723 } 724 newPath := unmergedPath.ChildPath( 725 cop.obfuscatedNewName(), unmergedChain.mostRecent, 726 unmergedChain.obfuscator) 727 if cop.Type == data.Dir { 728 // recurse for this chain 729 newPaths, err := cr.checkPathForMerge(ctx, unmergedChain, newPath, 730 unmergedChains, mergedChains) 731 if err != nil { 732 return nil, err 733 } 734 // Add any further subdirectories that need merging under this 735 // subdirectory. 736 newUnmergedPaths = append(newUnmergedPaths, newPaths...) 737 } else { 738 // Set the path for all child ops 739 unrefedOrig := false 740 for _, op := range unmergedChain.ops { 741 op.setFinalPath(newPath) 742 _, isSyncOp := op.(*syncOp) 743 // If a later write overwrites the original, take it 744 // out of the unmerged created list so it can be 745 // properly unreferenced. 746 if !unrefedOrig && isSyncOp { 747 unrefedOrig = true 748 delete(unmergedChains.createdOriginals, mergedOriginal) 749 } 750 } 751 } 752 // Then add this create's path. 753 newUnmergedPaths = append(newUnmergedPaths, newPath) 754 } 755 756 // Remove the unneeded create ops 757 if len(toDrop) > 0 { 758 newOps := make([]op, 0, len(unmergedChain.ops)-len(toDrop)) 759 for i, op := range unmergedChain.ops { 760 if toDrop[i] { 761 cr.log.CDebugf(ctx, 762 "Dropping double create unmerged operation: %s", op) 763 } else { 764 newOps = append(newOps, op) 765 } 766 } 767 unmergedChain.ops = newOps 768 } 769 770 return newUnmergedPaths, nil 771 } 772 773 // findCreatedDirsToMerge finds directories that were created in both 774 // the unmerged and merged branches, and resets the original unmerged 775 // pointer to match the original merged pointer. It returns a slice of 776 // new unmerged paths that need to be combined with the unmergedPaths 777 // slice. 778 func (cr *ConflictResolver) findCreatedDirsToMerge(ctx context.Context, 779 unmergedPaths []data.Path, unmergedChains, mergedChains *crChains) ( 780 []data.Path, error) { 781 var newUnmergedPaths []data.Path 782 for _, unmergedPath := range unmergedPaths { 783 unmergedChain, ok := 784 unmergedChains.byMostRecent[unmergedPath.TailPointer()] 785 if !ok { 786 return nil, fmt.Errorf("findCreatedDirsToMerge: No unmerged chain "+ 787 "for most recent %v", unmergedPath.TailPointer()) 788 } 789 790 newPaths, err := cr.checkPathForMerge(ctx, unmergedChain, unmergedPath, 791 unmergedChains, mergedChains) 792 if err != nil { 793 return nil, err 794 } 795 newUnmergedPaths = append(newUnmergedPaths, newPaths...) 796 } 797 return newUnmergedPaths, nil 798 } 799 800 type createMapKey struct { 801 ptr data.BlockPointer 802 name string 803 } 804 805 // addChildBlocksIfIndirectFile adds refblocks for all child blocks of 806 // the given file. It will return an error if called with a pointer 807 // that doesn't represent a file. 808 func (cr *ConflictResolver) addChildBlocksIfIndirectFile( 809 ctx context.Context, lState *kbfssync.LockState, unmergedChains *crChains, 810 currPath data.Path, op op) error { 811 // For files with indirect pointers, add all child blocks 812 // as refblocks for the re-created file. 813 infos, err := cr.fbo.blocks.GetIndirectFileBlockInfos( 814 ctx, lState, unmergedChains.mostRecentChainMDInfo, currPath) 815 if err != nil { 816 return err 817 } 818 if len(infos) > 0 { 819 cr.log.CDebugf(ctx, "Adding child pointers for recreated "+ 820 "file %s", currPath) 821 for _, info := range infos { 822 op.AddRefBlock(info.BlockPointer) 823 } 824 } 825 return nil 826 } 827 828 // resolvedMergedPathTail takes an unmerged path, and returns as much 829 // of the tail-end of the corresponding merged path that it can, using 830 // only information within the chains. It may not be able to return a 831 // complete chain if, for example, a directory was changed in the 832 // unmerged branch but not in the merged branch, and so the merged 833 // chain would not have enough information to construct the merged 834 // branch completely. This function returns the partial path, as well 835 // as the most recent pointer to the first changed node in the merged 836 // chains (which can be subsequently used to find the beginning of the 837 // merged path). 838 // 839 // The given unmerged path should be for a node that wasn't created 840 // during the unmerged branch. 841 // 842 // It is also possible for directories used in the unmerged path to 843 // have been completely removed from the merged path. In this case, 844 // they need to be recreated. So this function also returns a slice 845 // of create ops that will need to be replayed in the merged branch 846 // for the conflicts to be resolved; all of these ops have their 847 // writer info set to the given one. 848 func (cr *ConflictResolver) resolveMergedPathTail(ctx context.Context, 849 lState *kbfssync.LockState, unmergedPath data.Path, 850 unmergedChains, mergedChains *crChains, 851 currUnmergedWriterInfo writerInfo) ( 852 data.Path, data.BlockPointer, []*createOp, error) { 853 unmergedOriginal, err := 854 unmergedChains.originalFromMostRecent(unmergedPath.TailPointer()) 855 if err != nil { 856 cr.log.CDebugf(ctx, "Couldn't find original pointer for %v", 857 unmergedPath.TailPointer()) 858 return data.Path{}, data.BlockPointer{}, nil, err 859 } 860 861 var recreateOps []*createOp // fill in backwards, and reverse at the end 862 currOriginal := unmergedOriginal 863 currPath := unmergedPath 864 mergedPath := data.Path{ 865 FolderBranch: unmergedPath.FolderBranch, 866 Path: nil, // fill in backwards, and reverse at the end 867 ChildObfuscator: cr.fbo.makeObfuscator(), 868 } 869 870 // First find the earliest merged parent. 871 for mergedChains.isDeleted(currOriginal) { 872 cr.log.CDebugf(ctx, "%v was deleted in the merged branch (%s)", 873 currOriginal, currPath) 874 if !currPath.HasValidParent() { 875 return data.Path{}, data.BlockPointer{}, nil, 876 fmt.Errorf("Couldn't find valid merged parent path for %v", 877 unmergedOriginal) 878 } 879 880 // If this node has been deleted, we need to search 881 // backwards in the path to find the latest node that 882 // hasn't been deleted and re-recreate nodes upward from 883 // there. 884 name := currPath.TailName() 885 mergedPath.Path = append(mergedPath.Path, data.PathNode{ 886 BlockPointer: currOriginal, 887 Name: name, 888 }) 889 parentPath := *currPath.ParentPath() 890 parentOriginal, err := 891 unmergedChains.originalFromMostRecent(parentPath.TailPointer()) 892 if err != nil { 893 cr.log.CDebugf(ctx, "Couldn't find original pointer for %v", 894 parentPath.TailPointer()) 895 return data.Path{}, data.BlockPointer{}, nil, err 896 } 897 898 // Drop the merged rmOp since we're recreating it, and we 899 // don't want to replay that notification locally. 900 if mergedChain, ok := mergedChains.byOriginal[parentOriginal]; ok { 901 mergedMostRecent, err := 902 mergedChains.mostRecentFromOriginalOrSame(currOriginal) 903 if err != nil { 904 return data.Path{}, data.BlockPointer{}, nil, err 905 } 906 outer: 907 for i, op := range mergedChain.ops { 908 ro, ok := op.(*rmOp) 909 if !ok { 910 continue 911 } 912 // Use the unref'd pointer, and not the name, to identify 913 // the operation, since renames might have happened on the 914 // merged branch. 915 for _, unref := range ro.Unrefs() { 916 if unref != mergedMostRecent { 917 continue 918 } 919 920 mergedChain.ops = 921 append(mergedChain.ops[:i], mergedChain.ops[i+1:]...) 922 break outer 923 } 924 } 925 } else { 926 // If there's no chain, then likely a previous resolution 927 // removed an entire directory tree, and so the individual 928 // rm operations aren't listed. In that case, there's no 929 // rm op to remove. 930 cr.log.CDebugf(ctx, "No corresponding merged chain for parent "+ 931 "%v; skipping rm removal", parentOriginal) 932 } 933 934 de, err := cr.fbo.blocks.GetEntry( 935 ctx, lState, unmergedChains.mostRecentChainMDInfo, currPath) 936 if err != nil { 937 return data.Path{}, data.BlockPointer{}, nil, err 938 } 939 co, err := newCreateOp(name.Plaintext(), parentOriginal, de.Type) 940 if err != nil { 941 return data.Path{}, data.BlockPointer{}, nil, err 942 } 943 co.AddSelfUpdate(parentOriginal) 944 co.setFinalPath(parentPath) 945 co.AddRefBlock(currOriginal) 946 co.setWriterInfo(currUnmergedWriterInfo) 947 948 if co.Type != data.Dir { 949 err = cr.addChildBlocksIfIndirectFile(ctx, lState, 950 unmergedChains, currPath, co) 951 if err != nil { 952 return data.Path{}, data.BlockPointer{}, nil, err 953 } 954 955 // Delete any sync/setattr ops on the removed, merged file. 956 if mergedChain, ok := mergedChains.byOriginal[currOriginal]; ok { 957 mergedChains.removeChain(mergedChain.mostRecent) 958 } 959 } 960 961 // If this happens to have been renamed on the unmerged 962 // branch, drop the rm half of the rename operation; just 963 // leave it as a create. 964 if ri, ok := unmergedChains.renamedOriginals[currOriginal]; ok { 965 oldParent, ok := unmergedChains.byOriginal[ri.originalOldParent] 966 if !ok { 967 cr.log.CDebugf(ctx, "Couldn't find chain for original "+ 968 "old parent: %v", ri.originalOldParent) 969 return data.Path{}, data.BlockPointer{}, nil, 970 errors.WithStack(NoChainFoundError{ri.originalOldParent}) 971 } 972 for _, op := range oldParent.ops { 973 ro, ok := op.(*rmOp) 974 if !ok { 975 continue 976 } 977 if ro.OldName == ri.oldName { 978 ro.dropThis = true 979 break 980 } 981 } 982 983 // Replace the create op with the new recreate op, 984 // which contains the proper refblock. 985 newParent, ok := unmergedChains.byOriginal[ri.originalNewParent] 986 if !ok { 987 cr.log.CDebugf(ctx, "Couldn't find chain for original new "+ 988 "parent: %v", ri.originalNewParent) 989 return data.Path{}, data.BlockPointer{}, nil, 990 errors.WithStack(NoChainFoundError{ri.originalNewParent}) 991 } 992 for i, op := range newParent.ops { 993 oldCo, ok := op.(*createOp) 994 if !ok { 995 continue 996 } 997 if oldCo.NewName == ri.newName { 998 newParent.ops[i] = co 999 break 1000 } 1001 } 1002 } else { 1003 recreateOps = append(recreateOps, co) 1004 } 1005 1006 currOriginal = parentOriginal 1007 currPath = parentPath 1008 } 1009 1010 // Now we have the latest pointer along the path that is 1011 // shared between the branches. Our next step is to find the 1012 // current merged path to the most recent version of that 1013 // original. We can do that as follows: 1014 // * If the pointer has been changed in the merged branch, we 1015 // can search for it later using fbo.blocks.SearchForNodes 1016 // * If it hasn't been changed, check if it has been renamed to 1017 // somewhere else. If so, use fbo.blocks.SearchForNodes on 1018 // that parent later. 1019 // * Otherwise, iterate up the path towards the root. 1020 var mostRecent data.BlockPointer 1021 for i := len(currPath.Path) - 1; i >= 0; i-- { 1022 currOriginal, err := unmergedChains.originalFromMostRecent( 1023 currPath.Path[i].BlockPointer) 1024 if err != nil { 1025 cr.log.CDebugf(ctx, "Couldn't find original pointer for %v", 1026 currPath.Path[i]) 1027 return data.Path{}, data.BlockPointer{}, nil, err 1028 } 1029 1030 // Has it changed in the merged branch? 1031 mostRecent, err = mergedChains.mostRecentFromOriginal(currOriginal) 1032 if err == nil { 1033 break 1034 } 1035 1036 mergedPath.Path = append(mergedPath.Path, data.PathNode{ 1037 BlockPointer: currOriginal, 1038 Name: currPath.Path[i].Name, 1039 }) 1040 1041 // Has it been renamed? 1042 if originalParent, newName, ok := 1043 mergedChains.renamedParentAndName(currOriginal); ok { 1044 cr.log.CDebugf(ctx, "%v has been renamed in the merged branch", 1045 currOriginal) 1046 mostRecentParent, err := 1047 mergedChains.mostRecentFromOriginal(originalParent) 1048 if err != nil { 1049 cr.log.CDebugf(ctx, "Couldn't find original pointer for %v", 1050 originalParent) 1051 return data.Path{}, data.BlockPointer{}, nil, err 1052 } 1053 mostRecent = mostRecentParent 1054 // update the name for this renamed node 1055 mergedPath.Path[len(mergedPath.Path)-1].Name = 1056 data.NewPathPartString(newName, mergedPath.Obfuscator()) 1057 break 1058 } 1059 } 1060 1061 // reverse the merged path 1062 for i, j := 0, len(mergedPath.Path)-1; i < j; i, j = i+1, j-1 { 1063 mergedPath.Path[i], mergedPath.Path[j] = 1064 mergedPath.Path[j], mergedPath.Path[i] 1065 } 1066 1067 // reverse recreateOps 1068 for i, j := 0, len(recreateOps)-1; i < j; i, j = i+1, j-1 { 1069 recreateOps[i], recreateOps[j] = recreateOps[j], recreateOps[i] 1070 } 1071 1072 return mergedPath, mostRecent, recreateOps, nil 1073 } 1074 1075 // resolveMergedPaths maps each tail most recent pointer for all the 1076 // given unmerged paths to a corresponding path in the merged branch. 1077 // The merged branch may be missing some nodes that have been deleted; 1078 // in that case, the merged path will contain placeholder path nodes 1079 // using the original pointers for those directories. 1080 // 1081 // This function also returns a set of createOps that can be used to 1082 // recreate the missing directories in the merged branch. If the 1083 // parent directory needing the create has been deleted, then the 1084 // unref ptr in the createOp contains the original pointer for the 1085 // directory rather than the most recent merged pointer. 1086 // 1087 // It also potentially returns a new slice of unmerged paths that the 1088 // caller should combine with the existing slice, corresponding to 1089 // deleted unmerged chains that still have relevant operations to 1090 // resolve. 1091 func (cr *ConflictResolver) resolveMergedPaths(ctx context.Context, 1092 lState *kbfssync.LockState, unmergedPaths []data.Path, 1093 unmergedChains, mergedChains *crChains, 1094 currUnmergedWriterInfo writerInfo) ( 1095 map[data.BlockPointer]data.Path, []*createOp, []data.Path, error) { 1096 // maps each most recent unmerged pointer to the corresponding 1097 // most recent merged path. 1098 mergedPaths := make(map[data.BlockPointer]data.Path) 1099 1100 chainsToSearchFor := make(map[data.BlockPointer][]data.BlockPointer) 1101 var ptrs []data.BlockPointer 1102 1103 // While we're at it, find any deleted unmerged directory chains 1104 // containing operations, where the corresponding merged chain has 1105 // changed. The unmerged rm ops will need to be re-applied in 1106 // that case. 1107 var newUnmergedPaths []data.Path 1108 for original, unmergedChain := range unmergedChains.byOriginal { 1109 if !unmergedChains.isDeleted(original) || len(unmergedChain.ops) == 0 || 1110 unmergedChain.isFile() { 1111 continue 1112 } 1113 mergedChain, ok := mergedChains.byOriginal[original] 1114 if !ok || len(mergedChain.ops) == 0 || 1115 mergedChains.isDeleted(original) { 1116 continue 1117 } 1118 1119 cr.log.CDebugf(ctx, "A modified unmerged path %v was deleted but "+ 1120 "also modified in the merged branch %v", 1121 unmergedChain.mostRecent, mergedChain.mostRecent) 1122 1123 // We know that everything in the directory has been removed, 1124 // so only rm ops matter. 1125 var newOps []op 1126 for _, op := range unmergedChain.ops { 1127 if rop, ok := op.(*rmOp); ok { 1128 newOps = append(newOps, rop) 1129 } 1130 } 1131 unmergedChain.ops = newOps 1132 1133 // Fake the unmerged path, it doesn't matter 1134 unmergedPath := data.Path{ 1135 FolderBranch: cr.fbo.folderBranch, 1136 Path: []data.PathNode{ 1137 {BlockPointer: unmergedChain.mostRecent}, 1138 }, 1139 ChildObfuscator: cr.fbo.makeObfuscator(), 1140 } 1141 chainsToSearchFor[mergedChain.mostRecent] = 1142 append(chainsToSearchFor[mergedChain.mostRecent], 1143 unmergedChain.mostRecent) 1144 ptrs = append(ptrs, mergedChain.mostRecent) 1145 newUnmergedPaths = append(newUnmergedPaths, unmergedPath) 1146 } 1147 1148 // Skip out early if there's nothing to do. 1149 if len(unmergedPaths) == 0 && len(ptrs) == 0 { 1150 return mergedPaths, nil, nil, nil 1151 } 1152 1153 // For each unmerged path, find the corresponding most recent 1154 // pointer in the merged path. Track which entries need to be 1155 // re-created. 1156 var recreateOps []*createOp 1157 createsSeen := make(map[createMapKey]bool) 1158 // maps a merged most recent pointer to the set of unmerged most 1159 // recent pointers that need some of their path filled in. 1160 for _, p := range unmergedPaths { 1161 mergedPath, mostRecent, ops, err := cr.resolveMergedPathTail( 1162 ctx, lState, p, unmergedChains, mergedChains, 1163 currUnmergedWriterInfo) 1164 if err != nil { 1165 return nil, nil, nil, err 1166 } 1167 1168 // Save any recreateOps we've haven't seen yet. 1169 for _, op := range ops { 1170 key := createMapKey{op.Dir.Unref, op.NewName} 1171 if _, ok := createsSeen[key]; ok { 1172 continue 1173 } 1174 createsSeen[key] = true 1175 recreateOps = append(recreateOps, op) 1176 } 1177 1178 // At the end of this process, we are left with a merged path 1179 // that begins just after mostRecent. We will fill this in 1180 // later with the searchFromNodes result. 1181 mergedPaths[p.TailPointer()] = mergedPath 1182 if !mergedPath.IsValid() { 1183 // Temporary debugging for KBFS-2507. 1184 cr.log.CDebugf(ctx, "Adding invalid merged path for %v "+ 1185 "(may be temporary)", p.TailPointer()) 1186 } 1187 1188 if mostRecent.IsInitialized() { 1189 // Remember to fill in the corresponding mergedPath once we 1190 // get mostRecent's full path. 1191 chainsToSearchFor[mostRecent] = 1192 append(chainsToSearchFor[mostRecent], p.TailPointer()) 1193 } 1194 } 1195 1196 // Now we can search for all the merged paths that need to be 1197 // updated due to unmerged operations. Start with a clean node 1198 // cache for the merged branch. 1199 newPtrs := make(map[data.BlockPointer]bool) 1200 for ptr := range mergedChains.byMostRecent { 1201 newPtrs[ptr] = true 1202 } 1203 for ptr := range chainsToSearchFor { 1204 ptrs = append(ptrs, ptr) 1205 } 1206 1207 if len(ptrs) == 0 { 1208 // Nothing to search for 1209 return mergedPaths, recreateOps, newUnmergedPaths, nil 1210 } 1211 1212 mergedNodeCache := newNodeCacheStandard(cr.fbo.folderBranch) 1213 mergedNodeCache.SetObfuscatorMaker(cr.fbo.makeObfuscator) 1214 nodeMap, _, err := cr.fbo.blocks.SearchForNodes( 1215 ctx, mergedNodeCache, ptrs, newPtrs, 1216 mergedChains.mostRecentChainMDInfo, 1217 mergedChains.mostRecentChainMDInfo.GetRootDirEntry().BlockPointer) 1218 if err != nil { 1219 return nil, nil, nil, err 1220 } 1221 1222 for ptr, n := range nodeMap { 1223 if n == nil { 1224 // All the pointers we're looking for should definitely be 1225 // findable in the merged branch somewhere. 1226 return nil, nil, nil, NodeNotFoundError{ptr} 1227 } 1228 1229 p := mergedNodeCache.PathFromNode(n) 1230 for _, unmergedMostRecent := range chainsToSearchFor[ptr] { 1231 // Prepend the found path to the existing path 1232 mergedPath := mergedPaths[unmergedMostRecent] 1233 if !mergedPath.IsValid() { 1234 // Temporary debugging for KBFS-2507. 1235 cr.log.CDebugf(ctx, "Populating merged path for %v with %v", 1236 unmergedMostRecent, p.Path) 1237 } 1238 1239 newPath := make([]data.PathNode, len(p.Path)+len(mergedPath.Path)) 1240 copy(newPath[:len(p.Path)], p.Path) 1241 copy(newPath[len(p.Path):], mergedPath.Path) 1242 mergedPath.FolderBranch = cr.fbo.folderBranch 1243 mergedPath.Path = newPath 1244 mergedPaths[unmergedMostRecent] = mergedPath 1245 1246 // update the final paths for those corresponding merged 1247 // chains 1248 mergedMostRecent := mergedPath.TailPointer() 1249 chain, ok := mergedChains.byMostRecent[mergedMostRecent] 1250 if !ok { 1251 // it's ok for the merged path not to exist because we 1252 // might still need to create it. 1253 continue 1254 } 1255 for _, op := range chain.ops { 1256 op.setFinalPath(mergedPath) 1257 } 1258 } 1259 } 1260 1261 return mergedPaths, recreateOps, newUnmergedPaths, nil 1262 } 1263 1264 // buildChainsAndPaths make crChains for both the unmerged and merged 1265 // branches since the branch point, the corresponding full paths for 1266 // those changes, any new recreate ops, and returns the MDs used to 1267 // compute all this. Note that even if err is nil, the merged MD list 1268 // might be non-nil to allow for better error handling. 1269 // 1270 // This always returns the merged MDs, even in an error case, to allow 1271 // the caller's error-handling code to unstage if necessary. 1272 func (cr *ConflictResolver) buildChainsAndPaths( 1273 ctx context.Context, lState *kbfssync.LockState, writerLocked bool) ( 1274 unmergedChains, mergedChains *crChains, unmergedPaths []data.Path, 1275 mergedPaths map[data.BlockPointer]data.Path, recreateOps []*createOp, 1276 unmerged, merged []ImmutableRootMetadata, err error) { 1277 // Fetch the merged and unmerged MDs 1278 unmerged, merged, err = cr.getMDs(ctx, lState, writerLocked) 1279 if err != nil { 1280 return nil, nil, nil, nil, nil, nil, nil, err 1281 } 1282 1283 if len(unmerged) == 0 { 1284 cr.log.CDebugf(ctx, "Skipping merge process due to empty MD list") 1285 return nil, nil, nil, nil, nil, nil, merged, nil 1286 } 1287 1288 // Update the current input to reflect the MDs we'll actually be 1289 // working with. 1290 err = cr.updateCurrInput(ctx, unmerged, merged) 1291 if err != nil { 1292 return nil, nil, nil, nil, nil, nil, merged, err 1293 } 1294 1295 // Canceled before we start the heavy lifting? 1296 err = cr.checkDone(ctx) 1297 if err != nil { 1298 return nil, nil, nil, nil, nil, nil, merged, err 1299 } 1300 1301 // Make the chains 1302 unmergedChains, mergedChains, err = cr.makeChains(ctx, unmerged, merged) 1303 if err != nil { 1304 return nil, nil, nil, nil, nil, nil, merged, err 1305 } 1306 1307 // TODO: if the root node didn't change in either chain, we can 1308 // short circuit the rest of the process with a really easy 1309 // merge... 1310 1311 // Get the full path for every most recent unmerged pointer with a 1312 // chain of unmerged operations, and which was not created or 1313 // deleted within in the unmerged branch. 1314 unmergedPaths, err = unmergedChains.getPaths(ctx, &cr.fbo.blocks, 1315 cr.log, cr.fbo.nodeCache, false, cr.config.Mode().IsTestMode()) 1316 if err != nil { 1317 return nil, nil, nil, nil, nil, nil, merged, err 1318 } 1319 1320 // Add in any directory paths that were created in both branches. 1321 newUnmergedPaths, err := cr.findCreatedDirsToMerge(ctx, unmergedPaths, 1322 unmergedChains, mergedChains) 1323 if err != nil { 1324 return nil, nil, nil, nil, nil, nil, merged, err 1325 } 1326 unmergedPaths = append(unmergedPaths, newUnmergedPaths...) 1327 if len(newUnmergedPaths) > 0 { 1328 sort.Sort(crSortedPaths(unmergedPaths)) 1329 } 1330 1331 // Mark the recreate ops as being authored by the current user. 1332 kbpki := cr.config.KBPKI() 1333 session, err := kbpki.GetCurrentSession(ctx) 1334 if err != nil { 1335 return nil, nil, nil, nil, nil, nil, merged, err 1336 } 1337 1338 currUnmergedWriterInfo := newWriterInfo( 1339 session.UID, session.VerifyingKey, unmerged[len(unmerged)-1].Revision(), 1340 cr.fbo.oa()) 1341 1342 // Find the corresponding path in the merged branch for each of 1343 // these unmerged paths, and the set of any createOps needed to 1344 // apply these unmerged operations in the merged branch. 1345 mergedPaths, recreateOps, newUnmergedPaths, err = cr.resolveMergedPaths( 1346 ctx, lState, unmergedPaths, unmergedChains, mergedChains, 1347 currUnmergedWriterInfo) 1348 if err != nil { 1349 return nil, nil, nil, nil, nil, nil, merged, err 1350 } 1351 unmergedPaths = append(unmergedPaths, newUnmergedPaths...) 1352 if len(newUnmergedPaths) > 0 { 1353 sort.Sort(crSortedPaths(unmergedPaths)) 1354 } 1355 1356 return unmergedChains, mergedChains, unmergedPaths, mergedPaths, 1357 recreateOps, unmerged, merged, nil 1358 } 1359 1360 // addRecreateOpsToUnmergedChains inserts each recreateOp, into its 1361 // appropriate unmerged chain, creating one if it doesn't exist yet. 1362 // It also adds entries as necessary to mergedPaths, and returns a 1363 // slice of new unmergedPaths to be added. 1364 func (cr *ConflictResolver) addRecreateOpsToUnmergedChains(ctx context.Context, 1365 recreateOps []*createOp, unmergedChains, mergedChains *crChains, 1366 mergedPaths map[data.BlockPointer]data.Path) ([]data.Path, error) { 1367 if len(recreateOps) == 0 { 1368 return nil, nil 1369 } 1370 1371 // First create a lookup table that maps every block pointer in 1372 // every merged path to a corresponding key in the mergedPaths map. 1373 keys := make(map[data.BlockPointer]data.BlockPointer) 1374 for ptr, p := range mergedPaths { 1375 for _, node := range p.Path { 1376 keys[node.BlockPointer] = ptr 1377 } 1378 } 1379 1380 var newUnmergedPaths []data.Path 1381 for _, rop := range recreateOps { 1382 // If rop.Dir.Unref is a merged most recent pointer, look up the 1383 // original. Otherwise rop.Dir.Unref is the original. Use the 1384 // original to look up the appropriate unmerged chain and stick 1385 // this op at the front. 1386 origTargetPtr, err := 1387 mergedChains.originalFromMostRecentOrSame(rop.Dir.Unref) 1388 if err != nil { 1389 return nil, err 1390 } 1391 1392 chain, ok := unmergedChains.byOriginal[origTargetPtr] 1393 if !ok { 1394 return nil, fmt.Errorf("recreateOp for %v has no chain", 1395 origTargetPtr) 1396 } 1397 if len(chain.ops) == 0 { 1398 newUnmergedPaths = append(newUnmergedPaths, rop.getFinalPath()) 1399 } 1400 chain.ops = append([]op{rop}, chain.ops...) 1401 1402 // Look up the corresponding unmerged most recent pointer, and 1403 // check whether there's a merged path for it yet. If not, 1404 // create one by looking it up in the lookup table (created 1405 // above) and taking the appropriate subpath. 1406 _, ok = mergedPaths[chain.mostRecent] 1407 if !ok { 1408 mergedMostRecent := chain.original 1409 if !mergedChains.isDeleted(chain.original) { 1410 if mChain, ok := mergedChains.byOriginal[chain.original]; ok { 1411 mergedMostRecent = mChain.mostRecent 1412 } 1413 } 1414 key, ok := keys[mergedMostRecent] 1415 if !ok { 1416 return nil, fmt.Errorf("Couldn't find a merged path "+ 1417 "containing the target of a recreate op: %v", 1418 mergedMostRecent) 1419 } 1420 currPath := mergedPaths[key] 1421 for currPath.TailPointer() != mergedMostRecent && 1422 currPath.HasValidParent() { 1423 currPath = *currPath.ParentPath() 1424 } 1425 mergedPaths[chain.mostRecent] = currPath 1426 } 1427 } 1428 1429 return newUnmergedPaths, nil 1430 } 1431 1432 // convertCreateIntoSymlink finds the create operation for the given 1433 // node in the chain, and makes it into one that creates a new symlink 1434 // (for directories) or a file copy. It also removes the 1435 // corresponding remove operation from the old parent chain. 1436 func (cr *ConflictResolver) convertCreateIntoSymlinkOrCopy(ctx context.Context, 1437 ptr data.BlockPointer, info renameInfo, chain *crChain, 1438 unmergedChains, mergedChains *crChains, symPath string) error { 1439 found := false 1440 outer: 1441 for _, op := range chain.ops { 1442 if cop, ok := op.(*createOp); ok { 1443 if !cop.renamed || cop.NewName != info.newName { 1444 continue 1445 } 1446 1447 oldType := cop.Type 1448 if cop.Type == data.Dir { 1449 cop.Type = data.Sym 1450 cop.crSymPath = symPath 1451 cop.RefBlocks = nil 1452 } else { 1453 cop.forceCopy = true 1454 } 1455 cop.renamed = false 1456 1457 newInfo := renameInfo{ 1458 originalOldParent: info.originalNewParent, 1459 oldName: info.newName, 1460 originalNewParent: info.originalOldParent, 1461 newName: info.oldName, 1462 } 1463 if newInfo2, ok := mergedChains.renamedOriginals[ptr]; ok { 1464 // If this node was already moved in the merged 1465 // branch, we need to tweak the merged branch's rename 1466 // info so that it looks like it's being renamed from 1467 // the new unmerged location. 1468 newInfo = newInfo2 1469 newInfo.originalOldParent = info.originalNewParent 1470 newInfo.oldName = info.newName 1471 } else { 1472 // invert the op in the merged chains 1473 invertCreate, err := newRmOp(info.newName, 1474 info.originalNewParent, oldType) 1475 if err != nil { 1476 return err 1477 } 1478 err = invertCreate.Dir.setRef(info.originalNewParent) 1479 if err != nil { 1480 return err 1481 } 1482 invertRm, err := newCreateOp(info.oldName, 1483 info.originalOldParent, cop.Type) 1484 if err != nil { 1485 return err 1486 } 1487 err = invertRm.Dir.setRef(info.originalOldParent) 1488 if err != nil { 1489 return err 1490 } 1491 invertRm.renamed = true 1492 invertRm.AddRefBlock(ptr) 1493 1494 mergedNewMostRecent, err := mergedChains. 1495 mostRecentFromOriginalOrSame(info.originalNewParent) 1496 if err != nil { 1497 return err 1498 } 1499 mergedOldMostRecent, err := mergedChains. 1500 mostRecentFromOriginalOrSame(info.originalOldParent) 1501 if err != nil { 1502 return err 1503 } 1504 err = prependOpsToChain( 1505 mergedOldMostRecent, mergedChains, invertRm) 1506 if err != nil { 1507 return err 1508 } 1509 err = prependOpsToChain( 1510 mergedNewMostRecent, mergedChains, invertCreate) 1511 if err != nil { 1512 return err 1513 } 1514 } 1515 cr.log.CDebugf(ctx, "Putting new merged rename info "+ 1516 "%v -> %v (symPath: %v)", ptr, newInfo, 1517 data.NewPathPartString(symPath, chain.obfuscator)) 1518 mergedChains.renamedOriginals[ptr] = newInfo 1519 1520 // Fix up the corresponding rmOp to make sure 1521 // that it gets dropped 1522 oldParentChain := 1523 unmergedChains.byOriginal[info.originalOldParent] 1524 for _, oldOp := range oldParentChain.ops { 1525 ro, ok := oldOp.(*rmOp) 1526 if !ok { 1527 continue 1528 } 1529 if ro.OldName == info.oldName { 1530 // No need to copy since this createOp 1531 // must have been created as part of 1532 // conflict resolution. 1533 ro.dropThis = true 1534 break 1535 } 1536 } 1537 1538 found = true 1539 break outer 1540 } 1541 } 1542 if !found { 1543 return fmt.Errorf("fixRenameConflicts: couldn't find "+ 1544 "rename op corresponding to %v,%s", ptr, info.newName) 1545 } 1546 return nil 1547 } 1548 1549 // crConflictCheckQuick checks whether the two given chains have any 1550 // direct conflicts. TODO: currently this is a little pessimistic 1551 // because it assumes any set attrs are in conflict, when in reality 1552 // they can be for different attributes, or the same attribute with 1553 // the same value. 1554 func crConflictCheckQuick(unmergedChain, mergedChain *crChain) bool { 1555 return unmergedChain != nil && mergedChain != nil && 1556 ((unmergedChain.hasSyncOp() && mergedChain.hasSyncOp()) || 1557 (unmergedChain.hasSetAttrOp() && mergedChain.hasSetAttrOp())) 1558 } 1559 1560 func (cr *ConflictResolver) getSingleUnmergedPath( 1561 ctx context.Context, unmergedChains *crChains, chain *crChain) ( 1562 data.Path, error) { 1563 // Reuse some code by creating a new chains object 1564 // consisting of only this node. 1565 newChains := newCRChainsEmpty(cr.fbo.makeObfuscator) 1566 newChains.byOriginal[chain.original] = chain 1567 newChains.byMostRecent[chain.mostRecent] = chain 1568 // Fake out the rest of the chains to populate newPtrs. 1569 for _, c := range unmergedChains.byOriginal { 1570 if c.original == chain.original { 1571 continue 1572 } 1573 newChain := &crChain{ 1574 original: c.original, 1575 mostRecent: c.mostRecent, 1576 obfuscator: newChains.makeObfuscator(), 1577 } 1578 newChains.byOriginal[c.original] = newChain 1579 newChains.byMostRecent[c.mostRecent] = newChain 1580 } 1581 newChains.mostRecentChainMDInfo = unmergedChains.mostRecentChainMDInfo 1582 unmergedPaths, err := newChains.getPaths(ctx, &cr.fbo.blocks, 1583 cr.log, cr.fbo.nodeCache, false, cr.config.Mode().IsTestMode()) 1584 if err != nil { 1585 return data.Path{}, err 1586 } 1587 1588 if len(unmergedPaths) != 1 { 1589 return data.Path{}, fmt.Errorf("Couldn't find the unmerged path for %v", 1590 chain.original) 1591 } 1592 return unmergedPaths[0], nil 1593 } 1594 1595 // fixRenameConflicts checks every unmerged createOp associated with a 1596 // rename to see if it will cause a cycle. If so, it makes it a 1597 // symlink create operation instead. It also checks whether a 1598 // particular node had been renamed in both branches; if so, it will 1599 // copy files, and use symlinks for directories. 1600 func (cr *ConflictResolver) fixRenameConflicts(ctx context.Context, 1601 unmergedChains, mergedChains *crChains, 1602 mergedPaths map[data.BlockPointer]data.Path) ([]data.Path, error) { 1603 // For every renamed block pointer in the unmerged chains: 1604 // * Check if any BlockPointer in its merged path contains a relative of 1605 // itself 1606 // * If so, replace the corresponding unmerged create operation with a 1607 // symlink creation to the new merged path instead. 1608 // So, if in the merged branch someone did `mv b/ a/` and in the unmerged 1609 // branch someone did `mv a/ b/`, the conflict resolution would end up with 1610 // `a/b/a` where the second a is a symlink to "../". 1611 // 1612 // To calculate what the symlink should be, consider the following: 1613 // * The unmerged path for the new parent of ptr P is u_1/u_2/.../u_n 1614 // * u_i is the largest i <= n such that the corresponding block 1615 // can be mapped to a node in merged branch (pointer m_j). 1616 // * The full path to m_j in the merged branch is m_1/m_2/m_3/.../m_j 1617 // * For a rename cycle to occur, some m_x where x <= j must be a 1618 // descendant of P's original pointer. 1619 // * The full merged path to the parent of the second copy of P will 1620 // then be: m_1/m_2/.../m_x/.../m_j/u_i+1/.../u_n. 1621 // * Then, the symlink to put under P's name in u_n is "../"*((n-i)+(j-x)) 1622 // In the case that u_n is a directory that was newly-created in the 1623 // unmerged branch, we also need to construct a complete corresponding 1624 // merged path, for use in later stages (like executing actions). This 1625 // merged path is just m_1/.../m_j/u_i+1/.../u_n, using the most recent 1626 // unmerged pointers. 1627 var newUnmergedPaths []data.Path 1628 var removeRenames []data.BlockPointer 1629 var doubleRenames []data.BlockPointer // merged most recent ptrs 1630 for ptr, info := range unmergedChains.renamedOriginals { 1631 if unmergedChains.isDeleted(ptr) { 1632 continue 1633 } 1634 1635 // Also, we need to get the merged paths for anything that was 1636 // renamed in both branches, if they are different. 1637 if mergedInfo, ok := mergedChains.renamedOriginals[ptr]; ok && 1638 (info.originalNewParent != mergedInfo.originalNewParent || 1639 info.newName != mergedInfo.newName) { 1640 mergedMostRecent, err := 1641 mergedChains.mostRecentFromOriginalOrSame(ptr) 1642 if err != nil { 1643 return nil, err 1644 } 1645 1646 doubleRenames = append(doubleRenames, mergedMostRecent) 1647 continue 1648 } 1649 1650 // If this node was modified in both branches, we need to fork 1651 // the node, so we can get rid of the unmerged remove op and 1652 // force a copy on the create op. 1653 unmergedChain := unmergedChains.byOriginal[ptr] 1654 mergedChain := mergedChains.byOriginal[ptr] 1655 if crConflictCheckQuick(unmergedChain, mergedChain) { 1656 cr.log.CDebugf(ctx, "File that was renamed on the unmerged "+ 1657 "branch from %s -> %s has conflicting edits, forking "+ 1658 "(original ptr %v)", 1659 data.NewPathPartString(info.oldName, unmergedChain.obfuscator), 1660 data.NewPathPartString(info.newName, unmergedChain.obfuscator), 1661 ptr) 1662 oldParent := unmergedChains.byOriginal[info.originalOldParent] 1663 for _, op := range oldParent.ops { 1664 ro, ok := op.(*rmOp) 1665 if !ok { 1666 continue 1667 } 1668 if ro.OldName == info.oldName { 1669 ro.dropThis = true 1670 break 1671 } 1672 } 1673 newParent := unmergedChains.byOriginal[info.originalNewParent] 1674 for _, npOp := range newParent.ops { 1675 co, ok := npOp.(*createOp) 1676 if !ok { 1677 continue 1678 } 1679 if co.NewName == info.newName && co.renamed { 1680 co.forceCopy = true 1681 co.renamed = false 1682 co.AddRefBlock(unmergedChain.mostRecent) 1683 co.DelRefBlock(ptr) 1684 // Clear out the ops on the file itself, as we 1685 // will be doing a fresh create instead. 1686 unmergedChain.ops = nil 1687 break 1688 } 1689 } 1690 // Reset the chain of the forked file to the most recent 1691 // pointer, since we want to avoid any local notifications 1692 // linking the old version of the file to the new one. 1693 if ptr != unmergedChain.mostRecent { 1694 err := unmergedChains.changeOriginal( 1695 ptr, unmergedChain.mostRecent) 1696 if err != nil { 1697 return nil, err 1698 } 1699 unmergedChains.createdOriginals[unmergedChain.mostRecent] = true 1700 } 1701 continue 1702 } 1703 1704 // The merged path is keyed by the most recent unmerged tail 1705 // pointer. 1706 parent, err := 1707 unmergedChains.mostRecentFromOriginal(info.originalNewParent) 1708 if err != nil { 1709 return nil, err 1710 } 1711 1712 mergedPath, ok := mergedPaths[parent] 1713 unmergedWalkBack := 0 // (n-i) in the equation above 1714 var unmergedPath data.Path 1715 if !ok { 1716 // If this parent was newly created in the unmerged 1717 // branch, we need to look up its earliest parent that 1718 // existed in both branches. 1719 if !unmergedChains.isCreated(info.originalNewParent) { 1720 // There should definitely be a merged path for this 1721 // parent, since it doesn't have a create operation. 1722 return nil, fmt.Errorf("fixRenameConflicts: couldn't find "+ 1723 "merged path for %v", parent) 1724 } 1725 1726 chain := unmergedChains.byOriginal[info.originalNewParent] 1727 unmergedPath, err = cr.getSingleUnmergedPath( 1728 ctx, unmergedChains, chain) 1729 if err != nil { 1730 return nil, err 1731 } 1732 // Look backwards to find the first parent with a merged path. 1733 n := len(unmergedPath.Path) - 1 1734 for i := n; i >= 0; i-- { 1735 mergedPath, ok = mergedPaths[unmergedPath.Path[i].BlockPointer] 1736 if ok { 1737 unmergedWalkBack = n - i 1738 break 1739 } 1740 } 1741 if !ok { 1742 return nil, fmt.Errorf("fixRenameConflicts: couldn't find any "+ 1743 "merged path for any parents of %v", parent) 1744 } 1745 } 1746 1747 for x, pn := range mergedPath.Path { 1748 original, err := 1749 mergedChains.originalFromMostRecent(pn.BlockPointer) 1750 if err != nil { 1751 // This node wasn't changed in the merged branch 1752 original = pn.BlockPointer 1753 } 1754 1755 if original != ptr { 1756 continue 1757 } 1758 1759 // If any node on this path matches the renamed pointer, 1760 // we have a cycle. 1761 chain, ok := unmergedChains.byMostRecent[parent] 1762 if !ok { 1763 return nil, fmt.Errorf("fixRenameConflicts: no chain for "+ 1764 "parent %v", parent) 1765 } 1766 1767 j := len(mergedPath.Path) - 1 1768 // (j-x) in the above equation 1769 mergedWalkBack := j - x 1770 walkBack := unmergedWalkBack + mergedWalkBack 1771 1772 // Mark this as a symlink, and the resolver 1773 // will take care of making it a symlink in 1774 // the merged branch later. No need to copy 1775 // since this createOp must have been created 1776 // as part of conflict resolution. 1777 symPath := "./" + strings.Repeat("../", walkBack) 1778 cr.log.CDebugf(ctx, "Creating symlink %s at merged path %s", 1779 data.NewPathPartString(symPath, chain.obfuscator), mergedPath) 1780 1781 err = cr.convertCreateIntoSymlinkOrCopy(ctx, ptr, info, chain, 1782 unmergedChains, mergedChains, symPath) 1783 if err != nil { 1784 return nil, err 1785 } 1786 1787 if unmergedWalkBack > 0 { 1788 cr.log.CDebugf(ctx, "Adding new unmerged path %s", 1789 unmergedPath) 1790 newUnmergedPaths = append(newUnmergedPaths, 1791 unmergedPath) 1792 // Fake a merged path to make sure these 1793 // actions will be taken. 1794 mergedLen := len(mergedPath.Path) 1795 pLen := mergedLen + unmergedWalkBack 1796 p := data.Path{ 1797 FolderBranch: mergedPath.FolderBranch, 1798 Path: make([]data.PathNode, pLen), 1799 ChildObfuscator: cr.fbo.makeObfuscator(), 1800 } 1801 unmergedStart := len(unmergedPath.Path) - 1802 unmergedWalkBack 1803 copy(p.Path[:mergedLen], mergedPath.Path) 1804 copy(p.Path[mergedLen:], 1805 unmergedPath.Path[unmergedStart:]) 1806 mergedPaths[unmergedPath.TailPointer()] = p 1807 if !p.IsValid() { 1808 // Temporary debugging for KBFS-2507. 1809 cr.log.CDebugf(ctx, "Added invalid unmerged path for %v", 1810 unmergedPath.TailPointer()) 1811 } 1812 } 1813 1814 removeRenames = append(removeRenames, ptr) 1815 } 1816 } 1817 1818 // A map from merged most recent pointers of the parent 1819 // directories of files that have been forked, to a list of child 1820 // pointers within those directories that need their merged paths 1821 // fixed up. 1822 forkedFromMergedRenames := make(map[data.BlockPointer][]data.PathNode) 1823 1824 // Check the merged renames to see if any of them affect a 1825 // modified file that the unmerged branch did not rename. If we 1826 // find one, fork the file and leave the unmerged version under 1827 // its unmerged name. 1828 for ptr, info := range mergedChains.renamedOriginals { 1829 if mergedChains.isDeleted(ptr) { 1830 continue 1831 } 1832 1833 // Skip double renames, already dealt with them above. 1834 if unmergedInfo, ok := unmergedChains.renamedOriginals[ptr]; ok && 1835 (info.originalNewParent != unmergedInfo.originalNewParent || 1836 info.newName != unmergedInfo.newName) { 1837 continue 1838 } 1839 1840 // If this is a file that was modified in both branches, we 1841 // need to fork the file and tell the unmerged copy to keep 1842 // its current name. 1843 unmergedChain := unmergedChains.byOriginal[ptr] 1844 mergedChain := mergedChains.byOriginal[ptr] 1845 if crConflictCheckQuick(unmergedChain, mergedChain) { 1846 cr.log.CDebugf(ctx, "File that was renamed on the merged "+ 1847 "branch from %s -> %s has conflicting edits, forking "+ 1848 "(original ptr %v)", 1849 data.NewPathPartString(info.oldName, unmergedChain.obfuscator), 1850 data.NewPathPartString(info.newName, unmergedChain.obfuscator), 1851 ptr) 1852 var unmergedParentPath data.Path 1853 for _, op := range unmergedChain.ops { 1854 switch realOp := op.(type) { 1855 case *syncOp: 1856 realOp.keepUnmergedTailName = true 1857 unmergedParentPath = *op.getFinalPath().ParentPath() 1858 case *setAttrOp: 1859 realOp.keepUnmergedTailName = true 1860 unmergedParentPath = *op.getFinalPath().ParentPath() 1861 } 1862 } 1863 if unmergedParentPath.IsValid() { 1864 // Reset the merged path for this file back to the 1865 // merged path corresponding to the unmerged parent. 1866 // Put the merged parent path on the list of paths to 1867 // search for. 1868 unmergedParent := unmergedParentPath.TailPointer() 1869 if _, ok := mergedPaths[unmergedParent]; !ok { 1870 upOriginal := unmergedChains.originals[unmergedParent] 1871 mergedParent, err := 1872 mergedChains.mostRecentFromOriginalOrSame(upOriginal) 1873 if err != nil { 1874 return nil, err 1875 } 1876 oldPPS := data.NewPathPartString( 1877 info.oldName, unmergedParentPath.Obfuscator()) 1878 forkedFromMergedRenames[mergedParent] = 1879 append(forkedFromMergedRenames[mergedParent], 1880 data.PathNode{ 1881 BlockPointer: unmergedChain.mostRecent, 1882 Name: oldPPS, 1883 }) 1884 newUnmergedPaths = 1885 append(newUnmergedPaths, unmergedParentPath) 1886 } 1887 } 1888 } 1889 } 1890 1891 for _, ptr := range removeRenames { 1892 delete(unmergedChains.renamedOriginals, ptr) 1893 } 1894 1895 numRenamesToCheck := len(doubleRenames) + len(forkedFromMergedRenames) 1896 if numRenamesToCheck == 0 { 1897 return newUnmergedPaths, nil 1898 } 1899 1900 // Make chains for the new merged parents of all the double renames. 1901 newPtrs := make(map[data.BlockPointer]bool) 1902 ptrs := make([]data.BlockPointer, len(doubleRenames), numRenamesToCheck) 1903 copy(ptrs, doubleRenames) 1904 for ptr := range forkedFromMergedRenames { 1905 ptrs = append(ptrs, ptr) 1906 } 1907 // Fake out the rest of the chains to populate newPtrs 1908 for ptr := range mergedChains.byMostRecent { 1909 newPtrs[ptr] = true 1910 } 1911 1912 mergedNodeCache := newNodeCacheStandard(cr.fbo.folderBranch) 1913 mergedNodeCache.SetObfuscatorMaker(cr.fbo.makeObfuscator) 1914 nodeMap, _, err := cr.fbo.blocks.SearchForNodes( 1915 ctx, mergedNodeCache, ptrs, newPtrs, 1916 mergedChains.mostRecentChainMDInfo, 1917 mergedChains.mostRecentChainMDInfo.GetRootDirEntry().BlockPointer) 1918 if err != nil { 1919 return nil, err 1920 } 1921 1922 for _, ptr := range doubleRenames { 1923 // Find the merged paths 1924 node, ok := nodeMap[ptr] 1925 if !ok || node == nil { 1926 return nil, fmt.Errorf("Couldn't find merged path for "+ 1927 "doubly-renamed pointer %v", ptr) 1928 } 1929 1930 original, err := 1931 mergedChains.originalFromMostRecentOrSame(ptr) 1932 if err != nil { 1933 return nil, err 1934 } 1935 unmergedInfo, ok := unmergedChains.renamedOriginals[original] 1936 if !ok { 1937 return nil, fmt.Errorf("fixRenameConflicts: can't find the "+ 1938 "unmerged rename info for %v during double-rename resolution", 1939 original) 1940 } 1941 mergedInfo, ok := mergedChains.renamedOriginals[original] 1942 if !ok { 1943 return nil, fmt.Errorf("fixRenameConflicts: can't find the "+ 1944 "merged rename info for %v during double-rename resolution", 1945 original) 1946 } 1947 1948 // If any node on this path matches the renamed pointer, 1949 // we have a cycle. 1950 chain, ok := unmergedChains.byOriginal[unmergedInfo.originalNewParent] 1951 if !ok { 1952 return nil, fmt.Errorf("fixRenameConflicts: no chain for "+ 1953 "parent %v", unmergedInfo.originalNewParent) 1954 } 1955 1956 // For directories, the symlinks traverse down the merged path 1957 // to the first common node, and then back up to the new 1958 // parent/name. TODO: what happens when some element along 1959 // the merged path also got renamed by the unmerged branch? 1960 // The symlink would likely be wrong in that case. 1961 mergedPathOldParent, ok := mergedPaths[chain.mostRecent] 1962 if !ok { 1963 return nil, fmt.Errorf("fixRenameConflicts: couldn't find "+ 1964 "merged path for old parent %v", chain.mostRecent) 1965 } 1966 mergedPathNewParent := mergedNodeCache.PathFromNode(node) 1967 symPath := "./" 1968 newParentStart := 0 1969 outer: 1970 for i := len(mergedPathOldParent.Path) - 1; i >= 0; i-- { 1971 mostRecent := mergedPathOldParent.Path[i].BlockPointer 1972 for j, pnode := range mergedPathNewParent.Path { 1973 original, err := 1974 unmergedChains.originalFromMostRecentOrSame(mostRecent) 1975 if err != nil { 1976 return nil, err 1977 } 1978 mergedMostRecent, err := 1979 mergedChains.mostRecentFromOriginalOrSame(original) 1980 if err != nil { 1981 return nil, err 1982 } 1983 if pnode.BlockPointer == mergedMostRecent { 1984 newParentStart = j 1985 break outer 1986 } 1987 } 1988 symPath += "../" 1989 } 1990 // Move up directories starting from beyond the common parent, 1991 // to right before the actual node. 1992 for i := newParentStart + 1; i < len(mergedPathNewParent.Path)-1; i++ { 1993 symPath += mergedPathNewParent.Path[i].Name.Plaintext() + "/" 1994 } 1995 symPath += mergedInfo.newName 1996 1997 err = cr.convertCreateIntoSymlinkOrCopy(ctx, original, unmergedInfo, 1998 chain, unmergedChains, mergedChains, symPath) 1999 if err != nil { 2000 return nil, err 2001 } 2002 } 2003 2004 for ptr, pathNodes := range forkedFromMergedRenames { 2005 // Find the merged paths 2006 node, ok := nodeMap[ptr] 2007 if !ok || node == nil { 2008 return nil, fmt.Errorf("Couldn't find merged path for "+ 2009 "forked parent pointer %v", ptr) 2010 } 2011 2012 mergedPathNewParent := mergedNodeCache.PathFromNode(node) 2013 for _, pNode := range pathNodes { 2014 mergedPath := mergedPathNewParent.ChildPath( 2015 pNode.Name, pNode.BlockPointer, cr.fbo.makeObfuscator()) 2016 mergedPaths[pNode.BlockPointer] = mergedPath 2017 } 2018 } 2019 2020 return newUnmergedPaths, nil 2021 } 2022 2023 // addMergedRecreates drops any unmerged operations that remove a node 2024 // that was modified in the merged branch, and adds a create op to the 2025 // merged chain so that the node will be re-created locally. 2026 func (cr *ConflictResolver) addMergedRecreates(ctx context.Context, 2027 unmergedChains, mergedChains *crChains, 2028 mostRecentMergedWriterInfo writerInfo) error { 2029 for _, unmergedChain := range unmergedChains.byMostRecent { 2030 // First check for nodes that have been deleted in the unmerged 2031 // branch, but modified in the merged branch, and drop those 2032 // unmerged operations. 2033 for _, untypedOp := range unmergedChain.ops { 2034 ro, ok := untypedOp.(*rmOp) 2035 if !ok { 2036 continue 2037 } 2038 2039 // Perhaps the rm target has been renamed somewhere else, 2040 // before eventually being deleted. In this case, we have 2041 // to look up the original by iterating over 2042 // renamedOriginals. 2043 if len(ro.Unrefs()) == 0 { 2044 for original, info := range unmergedChains.renamedOriginals { 2045 if info.originalOldParent == unmergedChain.original && 2046 info.oldName == ro.OldName && 2047 unmergedChains.isDeleted(original) { 2048 ro.AddUnrefBlock(original) 2049 break 2050 } 2051 } 2052 } 2053 2054 for _, ptr := range ro.Unrefs() { 2055 unrefOriginal, err := 2056 unmergedChains.originalFromMostRecentOrSame(ptr) 2057 if err != nil { 2058 return err 2059 } 2060 2061 if c, ok := mergedChains.byOriginal[unrefOriginal]; ok { 2062 ro.dropThis = true 2063 // Need to prepend a create here to the merged parent, 2064 // in order catch any conflicts. 2065 parentOriginal := unmergedChain.original 2066 name := ro.OldName 2067 if newParent, newName, ok := 2068 mergedChains.renamedParentAndName(unrefOriginal); ok { 2069 // It was renamed in the merged branch, so 2070 // recreate with the new parent and new name. 2071 parentOriginal = newParent 2072 name = newName 2073 } else if info, ok := 2074 unmergedChains.renamedOriginals[unrefOriginal]; ok { 2075 // It was only renamed in the old parent, so 2076 // use the old parent and original name. 2077 parentOriginal = info.originalOldParent 2078 name = info.oldName 2079 } 2080 chain, ok := mergedChains.byOriginal[parentOriginal] 2081 if !ok { 2082 return fmt.Errorf("Couldn't find chain for parent %v "+ 2083 "of merged entry %v we're trying to recreate", 2084 parentOriginal, unrefOriginal) 2085 } 2086 t := data.Dir 2087 if c.isFile() { 2088 // TODO: how to fix this up for executables 2089 // and symlinks? Only matters for checking 2090 // conflicts if something with the same name 2091 // is created on the unmerged branch. 2092 t = data.File 2093 } 2094 co, err := newCreateOp(name, chain.original, t) 2095 if err != nil { 2096 return err 2097 } 2098 err = co.Dir.setRef(chain.original) 2099 if err != nil { 2100 return err 2101 } 2102 co.AddRefBlock(c.mostRecent) 2103 co.setWriterInfo(mostRecentMergedWriterInfo) 2104 chain.ensurePath(co, chain.mostRecent) 2105 chain.ops = append([]op{co}, chain.ops...) 2106 cr.log.CDebugf(ctx, "Re-created rm'd merge-modified node "+ 2107 "%v with operation %s in parent %v", unrefOriginal, co, 2108 parentOriginal) 2109 } 2110 } 2111 2112 } 2113 } 2114 return nil 2115 } 2116 2117 // getActionsToMerge returns the set of actions needed to merge each 2118 // unmerged chain of operations, in a map keyed by the tail pointer of 2119 // the corresponding merged path. 2120 func (cr *ConflictResolver) getActionsToMerge( 2121 ctx context.Context, unmergedChains, mergedChains *crChains, 2122 mergedPaths map[data.BlockPointer]data.Path) ( 2123 map[data.BlockPointer]crActionList, error) { 2124 actionMap := make(map[data.BlockPointer]crActionList) 2125 for unmergedMostRecent, unmergedChain := range unmergedChains.byMostRecent { 2126 original := unmergedChain.original 2127 // If this is a file that has been deleted in the merged 2128 // branch, a corresponding recreate op will take care of it, 2129 // no need to do anything here. 2130 2131 // We don't need the "ok" value from this lookup because it's 2132 // fine to pass a nil mergedChain into crChain.getActionsToMerge. 2133 mergedChain := mergedChains.byOriginal[original] 2134 mergedPath, ok := mergedPaths[unmergedMostRecent] 2135 if !ok { 2136 // This most likely means that the file was created or 2137 // deleted in the unmerged branch and thus has no 2138 // corresponding merged path yet. 2139 continue 2140 } 2141 if !mergedPath.IsValid() { 2142 cr.log.CWarningf(ctx, "Ignoring invalid merged path for %v "+ 2143 "(original=%v)", unmergedMostRecent, original) 2144 continue 2145 } 2146 2147 actions, err := unmergedChain.getActionsToMerge( 2148 ctx, cr.config.ConflictRenamer(), mergedPath, 2149 mergedChain) 2150 if err != nil { 2151 return nil, err 2152 } 2153 2154 if len(actions) > 0 { 2155 actionMap[mergedPath.TailPointer()] = actions 2156 } 2157 } 2158 2159 return actionMap, nil 2160 } 2161 2162 // collapseActions combines file updates with their parent directory 2163 // updates, because conflict resolution only happens within a 2164 // directory (i.e., files are merged directly, they are just 2165 // renamed/copied). It also collapses each action list to get rid of 2166 // redundant actions. It returns a slice of additional unmerged paths 2167 // that should be included in the overall list of unmergedPaths. 2168 func collapseActions(unmergedChains *crChains, unmergedPaths []data.Path, 2169 mergedPaths map[data.BlockPointer]data.Path, 2170 actionMap map[data.BlockPointer]crActionList) (newUnmergedPaths []data.Path) { 2171 for unmergedMostRecent, chain := range unmergedChains.byMostRecent { 2172 // Find the parent directory path and combine 2173 p, ok := mergedPaths[unmergedMostRecent] 2174 if !ok { 2175 continue 2176 } 2177 2178 fileActions := actionMap[p.TailPointer()] 2179 2180 // If this is a directory with setAttr(mtime)-related actions, 2181 // just those action should be collapsed into the parent. 2182 if !chain.isFile() { 2183 var parentActions crActionList 2184 var otherDirActions crActionList 2185 for _, action := range fileActions { 2186 moved := false 2187 switch realAction := action.(type) { 2188 case *copyUnmergedAttrAction: 2189 if realAction.attr[0] == mtimeAttr && !realAction.moved { 2190 realAction.moved = true 2191 parentActions = append(parentActions, realAction) 2192 moved = true 2193 } 2194 case *renameUnmergedAction: 2195 if realAction.causedByAttr == mtimeAttr && 2196 !realAction.moved { 2197 realAction.moved = true 2198 parentActions = append(parentActions, realAction) 2199 moved = true 2200 } 2201 } 2202 if !moved { 2203 otherDirActions = append(otherDirActions, action) 2204 } 2205 } 2206 if len(parentActions) == 0 { 2207 // A directory with no mtime actions, so treat it 2208 // normally. 2209 continue 2210 } 2211 fileActions = parentActions 2212 if len(otherDirActions) > 0 { 2213 actionMap[p.TailPointer()] = otherDirActions 2214 } else { 2215 delete(actionMap, p.TailPointer()) 2216 } 2217 } else { 2218 // Mark the copyUnmergedAttrActions as moved, so they 2219 // don't get moved again by the parent. 2220 for _, action := range fileActions { 2221 if realAction, ok := action.(*copyUnmergedAttrAction); ok { 2222 realAction.moved = true 2223 } 2224 } 2225 } 2226 2227 parentPath := *p.ParentPath() 2228 mergedParent := parentPath.TailPointer() 2229 parentActions, wasParentActions := actionMap[mergedParent] 2230 combinedActions := parentActions 2231 combinedActions = append(combinedActions, fileActions...) 2232 actionMap[mergedParent] = combinedActions 2233 if chain.isFile() { 2234 mergedPaths[unmergedMostRecent] = parentPath 2235 delete(actionMap, p.TailPointer()) 2236 } 2237 if !wasParentActions { 2238 // The parent isn't yet represented in our data 2239 // structures, so we have to make sure its actions get 2240 // executed. 2241 // 2242 // Find the unmerged path to get the unmerged parent. 2243 for _, unmergedPath := range unmergedPaths { 2244 if unmergedPath.TailPointer() != unmergedMostRecent { 2245 continue 2246 } 2247 unmergedParentPath := *unmergedPath.ParentPath() 2248 unmergedParent := unmergedParentPath.TailPointer() 2249 unmergedParentChain := 2250 unmergedChains.byMostRecent[unmergedParent] 2251 // If this is a file, only add a new unmerged path if 2252 // the parent has ops; otherwise it will confuse the 2253 // resolution code and lead to stray blocks. 2254 if !chain.isFile() || len(unmergedParentChain.ops) > 0 { 2255 newUnmergedPaths = 2256 append(newUnmergedPaths, unmergedParentPath) 2257 } 2258 // File merged paths were already updated above. 2259 if !chain.isFile() { 2260 mergedPaths[unmergedParent] = parentPath 2261 } 2262 break 2263 } 2264 } 2265 } 2266 2267 for ptr, actions := range actionMap { 2268 actionMap[ptr] = actions.collapse() 2269 } 2270 return newUnmergedPaths 2271 } 2272 2273 func (cr *ConflictResolver) computeActions(ctx context.Context, 2274 unmergedChains, mergedChains *crChains, unmergedPaths []data.Path, 2275 mergedPaths map[data.BlockPointer]data.Path, recreateOps []*createOp, 2276 mostRecentMergedWriterInfo writerInfo) ( 2277 map[data.BlockPointer]crActionList, []data.Path, error) { 2278 // Process all the recreateOps, adding them to the appropriate 2279 // unmerged chains. 2280 newUnmergedPaths, err := cr.addRecreateOpsToUnmergedChains( 2281 ctx, recreateOps, unmergedChains, mergedChains, mergedPaths) 2282 if err != nil { 2283 return nil, nil, err 2284 } 2285 2286 // Fix any rename cycles by turning the corresponding unmerged 2287 // createOp into a symlink entry type. 2288 moreNewUnmergedPaths, err := cr.fixRenameConflicts(ctx, unmergedChains, 2289 mergedChains, mergedPaths) 2290 if err != nil { 2291 return nil, nil, err 2292 } 2293 newUnmergedPaths = append(newUnmergedPaths, moreNewUnmergedPaths...) 2294 2295 // Recreate any modified merged nodes that were rm'd in the 2296 // unmerged branch. 2297 if err := cr.addMergedRecreates( 2298 ctx, unmergedChains, mergedChains, 2299 mostRecentMergedWriterInfo); err != nil { 2300 return nil, nil, err 2301 } 2302 2303 actionMap, err := cr.getActionsToMerge( 2304 ctx, unmergedChains, mergedChains, mergedPaths) 2305 if err != nil { 2306 return nil, nil, err 2307 } 2308 2309 // Finally, merged the file actions back into their parent 2310 // directory action list, and collapse everything together. 2311 moreNewUnmergedPaths = 2312 collapseActions(unmergedChains, unmergedPaths, mergedPaths, actionMap) 2313 return actionMap, append(newUnmergedPaths, moreNewUnmergedPaths...), nil 2314 } 2315 2316 func (cr *ConflictResolver) makeFileBlockDeepCopy(ctx context.Context, 2317 lState *kbfssync.LockState, chains *crChains, 2318 mergedMostRecent data.BlockPointer, parentPath data.Path, 2319 name data.PathPartString, ptr data.BlockPointer, blocks fileBlockMap, 2320 dirtyBcache data.DirtyBlockCacheSimple) (data.BlockPointer, error) { 2321 kmd := chains.mostRecentChainMDInfo 2322 2323 // Use a `nil` childObfuscator here, since this is for a file and 2324 // files can't have children to obfuscate, by defintion. 2325 file := parentPath.ChildPath(name, ptr, nil) 2326 oldInfos, err := cr.fbo.blocks.getIndirectFileBlockInfosLocked( 2327 ctx, lState, kmd, file) 2328 if err != nil { 2329 return data.BlockPointer{}, err 2330 } 2331 2332 newPtr, allChildPtrs, err := cr.fbo.blocks.deepCopyFileLocked( 2333 ctx, lState, kmd, file, dirtyBcache, cr.config.DataVersion()) 2334 if err != nil { 2335 return data.BlockPointer{}, err 2336 } 2337 2338 block, err := dirtyBcache.Get(ctx, cr.fbo.id(), newPtr, cr.fbo.branch()) 2339 if err != nil { 2340 return data.BlockPointer{}, err 2341 } 2342 fblock, isFileBlock := block.(*data.FileBlock) 2343 if !isFileBlock { 2344 return data.BlockPointer{}, NotFileBlockError{ptr, cr.fbo.branch(), file} 2345 } 2346 2347 // Mark this as having been created during this chain, so that 2348 // later during block accounting we can infer the origin of the 2349 // block. 2350 chains.createdOriginals[newPtr] = true 2351 // If this file was created within the branch, we should clean up 2352 // all the old block pointers. 2353 original, err := chains.originalFromMostRecentOrSame(ptr) 2354 if err != nil { 2355 return data.BlockPointer{}, err 2356 } 2357 newlyCreated := chains.isCreated(original) 2358 if newlyCreated { 2359 chains.toUnrefPointers[original] = true 2360 for _, oldInfo := range oldInfos { 2361 chains.toUnrefPointers[oldInfo.BlockPointer] = true 2362 } 2363 } 2364 2365 cr.log.CDebugf(ctx, "putTopBlock: %s", name) 2366 err = blocks.putTopBlock(ctx, mergedMostRecent, name, fblock) 2367 if err != nil { 2368 return data.BlockPointer{}, err 2369 } 2370 2371 for _, childPtr := range allChildPtrs { 2372 chains.createdOriginals[childPtr] = true 2373 } 2374 2375 return newPtr, nil 2376 } 2377 2378 func (cr *ConflictResolver) doOneAction( 2379 ctx context.Context, lState *kbfssync.LockState, 2380 unmergedChains, mergedChains *crChains, unmergedPath data.Path, 2381 mergedPaths map[data.BlockPointer]data.Path, chargedTo keybase1.UserOrTeamID, 2382 actionMap map[data.BlockPointer]crActionList, dbm dirBlockMap, 2383 doneActions map[data.BlockPointer]bool, newFileBlocks fileBlockMap, 2384 dirtyBcache data.DirtyBlockCacheSimple) error { 2385 unmergedMostRecent := unmergedPath.TailPointer() 2386 unmergedChain, ok := 2387 unmergedChains.byMostRecent[unmergedMostRecent] 2388 if !ok { 2389 return fmt.Errorf("Couldn't find unmerged chain for %v", 2390 unmergedMostRecent) 2391 } 2392 2393 // If this is a file that has been deleted in the merged 2394 // branch, a corresponding recreate op will take care of it, 2395 // no need to do anything here. 2396 2397 // find the corresponding merged path 2398 mergedPath, ok := mergedPaths[unmergedMostRecent] 2399 if !ok { 2400 // This most likely means that the file was created or 2401 // deleted in the unmerged branch and thus has no 2402 // corresponding merged path yet. 2403 return nil 2404 } 2405 if unmergedChain.isFile() { 2406 // The unmerged path is actually the parent (the merged 2407 // path was already corrected above). 2408 unmergedPath = *unmergedPath.ParentPath() 2409 } 2410 2411 // Now get the directory blocks. For unmerged directories, we 2412 // can use a nil local block cache, because unmerged blocks 2413 // should never be changed during the CR process (since 2414 // they're just going away). This call will lock `blockLock`, 2415 // and the subsequent `newDirData` calls can assume it's 2416 // locked already. 2417 var unmergedDir *data.DirData 2418 unmergedDir, cleanupFn := cr.fbo.blocks.newDirDataWithDBM( 2419 lState, unmergedPath, chargedTo, 2420 unmergedChains.mostRecentChainMDInfo, newDirBlockMapMemory()) 2421 defer cleanupFn() 2422 2423 if unmergedPath.TailPointer() == mergedPath.TailPointer() { 2424 // recreateOps update the merged paths using original 2425 // pointers; but if other stuff happened in the merged 2426 // block before it was deleted (such as other removes) we 2427 // want to preserve those. Therefore, we don't want the 2428 // unmerged block to remain in the local block cache. 2429 // Below we'll replace it with a new one instead. 2430 err := dbm.deleteBlock(ctx, unmergedPath.TailPointer()) 2431 if err != nil { 2432 return err 2433 } 2434 cr.log.CDebugf(ctx, "Removing block for %v from the local cache", 2435 unmergedPath.TailPointer()) 2436 } 2437 2438 blockExists, err := dbm.hasBlock(ctx, mergedPath.TailPointer()) 2439 if err != nil { 2440 return err 2441 } 2442 // If this is a recreate op and we haven't yet made a new 2443 // block for it, then make a new one and put it in the local 2444 // block cache. 2445 if mergedChains.isDeleted(mergedPath.TailPointer()) && !blockExists { 2446 err := dbm.putBlock( 2447 ctx, mergedPath.TailPointer(), data.NewDirBlock().(*data.DirBlock)) 2448 if err != nil { 2449 return err 2450 } 2451 } 2452 mergedDir := cr.fbo.blocks.newDirDataWithDBMLocked( 2453 lState, mergedPath, chargedTo, 2454 mergedChains.mostRecentChainMDInfo, dbm) 2455 // Force the top block into the `dbm`. `folderUpdatePrepper` 2456 // requires this, even if the block isn't modified, to 2457 // distinguish it from a file block. 2458 _, err = mergedDir.GetTopBlock(ctx, data.BlockWrite) 2459 if err != nil { 2460 return err 2461 } 2462 2463 actions := actionMap[mergedPath.TailPointer()] 2464 if len(actions) > 0 && !doneActions[mergedPath.TailPointer()] { 2465 // Make sure we don't try to execute the same actions twice. 2466 doneActions[mergedPath.TailPointer()] = true 2467 2468 // Any file block copies, keyed by their new temporary block 2469 // IDs, and later we will ready them. 2470 unmergedFetcher := func( 2471 ctx context.Context, name data.PathPartString, 2472 ptr data.BlockPointer) (data.BlockPointer, error) { 2473 return cr.makeFileBlockDeepCopy(ctx, lState, unmergedChains, 2474 mergedPath.TailPointer(), unmergedPath, name, ptr, 2475 newFileBlocks, dirtyBcache) 2476 } 2477 mergedFetcher := func( 2478 ctx context.Context, name data.PathPartString, 2479 ptr data.BlockPointer) (data.BlockPointer, error) { 2480 return cr.makeFileBlockDeepCopy(ctx, lState, mergedChains, 2481 mergedPath.TailPointer(), mergedPath, name, 2482 ptr, newFileBlocks, dirtyBcache) 2483 } 2484 2485 // Execute each action and save the modified ops back into 2486 // each chain. 2487 for _, action := range actions { 2488 // Make sure we don't get stuck inside a large action list 2489 // for a long time, if the actions are slow to complete. 2490 err := cr.checkDone(ctx) 2491 if err != nil { 2492 return err 2493 } 2494 2495 swap, newPtr, err := action.swapUnmergedBlock( 2496 ctx, unmergedChains, mergedChains, unmergedDir) 2497 if err != nil { 2498 return err 2499 } 2500 uDir := unmergedDir 2501 if swap { 2502 cr.log.CDebugf(ctx, "Swapping out dir %v for %v", 2503 newPtr, unmergedPath.TailPointer()) 2504 if newPtr == data.ZeroPtr { 2505 // Use the merged `dirData`. 2506 uDir = mergedDir 2507 } else { 2508 // Use the specified `dirData`, and supply a 2509 // `nil` local block cache to ensure that a) 2510 // only clean blocks are used, as blocks in 2511 // the `dbm` might have already been touched 2512 // by previous actions, and b) no new blocks 2513 // are cached. 2514 newPath := data.Path{ 2515 FolderBranch: mergedPath.FolderBranch, 2516 Path: []data.PathNode{{ 2517 BlockPointer: newPtr, Name: mergedPath.TailName()}}, 2518 ChildObfuscator: cr.fbo.makeObfuscator(), 2519 } 2520 uDir = cr.fbo.blocks.newDirDataWithDBMLocked( 2521 lState, newPath, chargedTo, 2522 mergedChains.mostRecentChainMDInfo, 2523 newDirBlockMapMemory()) 2524 } 2525 } 2526 2527 unrefs, err := action.do( 2528 ctx, unmergedFetcher, mergedFetcher, uDir, mergedDir) 2529 if err != nil { 2530 return err 2531 } 2532 for _, info := range unrefs { 2533 unmergedChains.toUnrefPointers[info.BlockPointer] = true 2534 } 2535 } 2536 } 2537 2538 // Now update the ops related to this exact path (not the ops 2539 // for its parent!). 2540 for _, action := range actions { 2541 // Make sure we don't get stuck inside a large action list 2542 // for a long time, if the actions are slow to complete. 2543 err := cr.checkDone(ctx) 2544 if err != nil { 2545 return err 2546 } 2547 2548 // unmergedMostRecent is for the correct pointer, but 2549 // mergedPath may be for the parent in the case of files 2550 // so we need to find the real mergedMostRecent pointer. 2551 mergedMostRecent := unmergedChain.original 2552 mergedChain, ok := mergedChains.byOriginal[unmergedChain.original] 2553 if ok { 2554 mergedMostRecent = mergedChain.mostRecent 2555 } 2556 2557 err = action.updateOps( 2558 ctx, unmergedMostRecent, mergedMostRecent, 2559 unmergedDir, mergedDir, unmergedChains, mergedChains) 2560 if err != nil { 2561 return err 2562 } 2563 } 2564 return nil 2565 } 2566 2567 func (cr *ConflictResolver) doActions(ctx context.Context, 2568 lState *kbfssync.LockState, unmergedChains, mergedChains *crChains, 2569 unmergedPaths []data.Path, mergedPaths map[data.BlockPointer]data.Path, 2570 actionMap map[data.BlockPointer]crActionList, dbm dirBlockMap, 2571 newFileBlocks fileBlockMap, dirtyBcache data.DirtyBlockCacheSimple) error { 2572 mergedMD := mergedChains.mostRecentChainMDInfo 2573 chargedTo, err := chargedToForTLF( 2574 ctx, cr.config.KBPKI(), cr.config.KBPKI(), cr.config, 2575 mergedMD.GetTlfHandle()) 2576 if err != nil { 2577 return err 2578 } 2579 2580 // For each set of actions: 2581 // * Find the corresponding chains 2582 // * Make a reference to each slice of ops 2583 // * Get the unmerged block. 2584 // * Get the merged block if it's not already in the local cache, and 2585 // make a copy. 2586 // * Get the merged block 2587 // * Do each action, updating the ops references to the returned ones 2588 // At the end, the local block cache should contain all the 2589 // updated merged blocks. A future phase will update the pointers 2590 // in standard Merkle-tree-fashion. 2591 doneActions := make(map[data.BlockPointer]bool) 2592 for _, unmergedPath := range unmergedPaths { 2593 // Make sure we don't get stuck inside a large unmerged list for 2594 // a long time, if the actions are slow to complete. 2595 err := cr.checkDone(ctx) 2596 if err != nil { 2597 return err 2598 } 2599 2600 err = cr.doOneAction( 2601 ctx, lState, unmergedChains, mergedChains, unmergedPath, 2602 mergedPaths, chargedTo, actionMap, dbm, doneActions, newFileBlocks, 2603 dirtyBcache) 2604 if err != nil { 2605 return err 2606 } 2607 } 2608 return nil 2609 } 2610 2611 type crRenameHelperKey struct { 2612 parentOriginal data.BlockPointer 2613 name string 2614 } 2615 2616 // makeRevertedOps changes the BlockPointers of the corresponding 2617 // operations for the given set of paths back to their originals, 2618 // which allows other parts of conflict resolution to more easily 2619 // build up the local and remote notifications needed. Also, it 2620 // reverts rm/create pairs back into complete rename operations, for 2621 // the purposes of notification, so this should only be called after 2622 // all conflicts and actions have been resolved. It returns the 2623 // complete slice of reverted operations. 2624 func (cr *ConflictResolver) makeRevertedOps(ctx context.Context, 2625 lState *kbfssync.LockState, sortedPaths []data.Path, chains *crChains, 2626 otherChains *crChains) ([]op, error) { 2627 var ops []op 2628 // Build a map of directory {original, name} -> renamed original. 2629 // This will help us map create ops to the corresponding old 2630 // parent. 2631 renames := make(map[crRenameHelperKey]data.BlockPointer) 2632 for original, ri := range chains.renamedOriginals { 2633 renames[crRenameHelperKey{ri.originalNewParent, ri.newName}] = original 2634 } 2635 2636 // Insert the operations starting closest to the root, so 2637 // necessary directories are created first. 2638 for i := len(sortedPaths) - 1; i >= 0; i-- { 2639 ptr := sortedPaths[i].TailPointer() 2640 chain, ok := chains.byMostRecent[ptr] 2641 if !ok { 2642 return nil, fmt.Errorf("makeRevertedOps: Couldn't find chain "+ 2643 "for %v", ptr) 2644 } 2645 2646 chainLoop: 2647 for _, op := range chain.ops { 2648 // Skip any rms that were part of a rename 2649 if rop, ok := op.(*rmOp); ok && len(rop.Unrefs()) == 0 { 2650 continue 2651 } 2652 2653 // Turn the create half of a rename back into a full rename. 2654 if cop, ok := op.(*createOp); ok && cop.renamed { 2655 renameOriginal, ok := renames[crRenameHelperKey{ 2656 chain.original, cop.NewName}] 2657 if !ok { 2658 if cop.crSymPath != "" || cop.Type == data.Sym { 2659 // For symlinks created by the CR process, we 2660 // expect the rmOp to have been removed. For 2661 // existing symlinks that were simply moved, 2662 // there is no benefit in combining their 2663 // create and rm ops back together since there 2664 // is no corresponding node. 2665 continue 2666 } 2667 return nil, fmt.Errorf("Couldn't find corresponding "+ 2668 "renamed original for %v, %s", 2669 chain.original, cop.NewName) 2670 } 2671 2672 if otherChains.isDeleted(renameOriginal) || 2673 chains.isCreated(renameOriginal) { 2674 // If we are re-instating a deleted node, or 2675 // dealing with a node that was created entirely 2676 // in this branch, just use the create op. 2677 op = chains.copyOpAndRevertUnrefsToOriginals(cop) 2678 if cop.Type != data.Dir { 2679 renameMostRecent, err := 2680 chains.mostRecentFromOriginalOrSame(renameOriginal) 2681 if err != nil { 2682 return nil, err 2683 } 2684 2685 err = cr.addChildBlocksIfIndirectFile(ctx, lState, 2686 chains, cop.getFinalPath().ChildPath( 2687 cop.obfuscatedNewName(), renameMostRecent, 2688 cr.fbo.makeObfuscator()), op) 2689 if err != nil { 2690 return nil, err 2691 } 2692 } 2693 } else { 2694 ri, ok := chains.renamedOriginals[renameOriginal] 2695 if !ok { 2696 return nil, fmt.Errorf("Couldn't find the rename info "+ 2697 "for original %v", renameOriginal) 2698 } 2699 2700 rop, err := newRenameOp(ri.oldName, ri.originalOldParent, 2701 ri.newName, ri.originalNewParent, renameOriginal, 2702 cop.Type) 2703 if err != nil { 2704 return nil, err 2705 } 2706 chain.ensurePath(rop, chain.mostRecent) 2707 // Set the Dir.Ref fields to be the same as the Unref 2708 // -- they will be fixed up later. 2709 rop.AddSelfUpdate(ri.originalOldParent) 2710 if ri.originalNewParent != ri.originalOldParent { 2711 rop.AddSelfUpdate(ri.originalNewParent) 2712 } 2713 for _, ptr := range cop.Unrefs() { 2714 origPtr, err := chains.originalFromMostRecentOrSame(ptr) 2715 if err != nil { 2716 return nil, err 2717 } 2718 rop.AddUnrefBlock(origPtr) 2719 } 2720 op = rop 2721 2722 // If this renames from a source that's been 2723 // deleted by a previous op, we should replace the 2724 // delete with this. 2725 for i, prevOp := range ops { 2726 rmop, ok := prevOp.(*rmOp) 2727 if !ok { 2728 continue 2729 } 2730 2731 if rop.OldDir.Unref == rmop.Dir.Unref && 2732 rop.OldName == rmop.OldName { 2733 ops[i] = op 2734 continue chainLoop 2735 } 2736 } 2737 2738 } 2739 } else { 2740 op = chains.copyOpAndRevertUnrefsToOriginals(op) 2741 // The dir of renamed setAttrOps must be reverted to 2742 // the new parent's original pointer. 2743 if sao, ok := op.(*setAttrOp); ok { 2744 if newDir, _, ok := 2745 otherChains.renamedParentAndName(sao.File); ok { 2746 err := sao.Dir.setUnref(newDir) 2747 if err != nil { 2748 return nil, err 2749 } 2750 } 2751 } 2752 } 2753 2754 ops = append(ops, op) 2755 } 2756 } 2757 2758 return ops, nil 2759 } 2760 2761 // createResolvedMD creates a MD update that will be merged into the 2762 // main folder as the resolving commit. It contains all of the 2763 // unmerged operations, as well as a "dummy" operation at the end 2764 // which will catch all of the BlockPointer updates. A later phase 2765 // will move all of those updates into their proper locations within 2766 // the other operations. 2767 func (cr *ConflictResolver) createResolvedMD(ctx context.Context, 2768 lState *kbfssync.LockState, unmergedPaths []data.Path, 2769 unmergedChains, mergedChains *crChains, 2770 mostRecentMergedMD ImmutableRootMetadata) (*RootMetadata, error) { 2771 err := cr.checkDone(ctx) 2772 if err != nil { 2773 return nil, err 2774 } 2775 2776 newMD, err := mostRecentMergedMD.MakeSuccessor( 2777 ctx, cr.config.MetadataVersion(), cr.config.Codec(), 2778 cr.config.KeyManager(), cr.config.KBPKI(), 2779 cr.config.KBPKI(), cr.config, mostRecentMergedMD.MdID(), true) 2780 if err != nil { 2781 return nil, err 2782 } 2783 2784 var newPaths []data.Path 2785 for original, chain := range unmergedChains.byOriginal { 2786 added := false 2787 for i, op := range chain.ops { 2788 if cop, ok := op.(*createOp); ok { 2789 // We need to add in any creates that happened 2790 // within newly-created directories (which aren't 2791 // being merged with other newly-created directories), 2792 // to ensure that the overall Refs are correct and 2793 // that future CR processes can check those create ops 2794 // for conflicts. 2795 if unmergedChains.isCreated(original) && 2796 !mergedChains.isCreated(original) { 2797 // Shallowly copy the create op and update its 2798 // directory to the most recent pointer -- this won't 2799 // work with the usual revert ops process because that 2800 // skips chains which are newly-created within this 2801 // branch. 2802 newCreateOp := *cop 2803 newCreateOp.Dir, err = makeBlockUpdate( 2804 chain.mostRecent, chain.mostRecent) 2805 if err != nil { 2806 return nil, err 2807 } 2808 chain.ops[i] = &newCreateOp 2809 if !added { 2810 newPaths = append(newPaths, data.Path{ 2811 FolderBranch: cr.fbo.folderBranch, 2812 Path: []data.PathNode{{ 2813 BlockPointer: chain.mostRecent}}, 2814 ChildObfuscator: cr.fbo.makeObfuscator(), 2815 }) 2816 added = true 2817 } 2818 } 2819 if cop.Type == data.Dir || len(cop.Refs()) == 0 { 2820 continue 2821 } 2822 // Add any direct file blocks too into each create op, 2823 // which originated in later unmerged syncs. 2824 ptr, err := 2825 unmergedChains.mostRecentFromOriginalOrSame(cop.Refs()[0]) 2826 if err != nil { 2827 return nil, err 2828 } 2829 trackSyncPtrChangesInCreate( 2830 ptr, chain, unmergedChains, cop.NewName) 2831 } 2832 } 2833 } 2834 if len(newPaths) > 0 { 2835 // Put the new paths at the beginning so they are processed 2836 // last in sorted order. 2837 unmergedPaths = append(newPaths, unmergedPaths...) 2838 } 2839 2840 ops, err := cr.makeRevertedOps( 2841 ctx, lState, unmergedPaths, unmergedChains, mergedChains) 2842 if err != nil { 2843 return nil, err 2844 } 2845 2846 cr.log.CDebugf(ctx, "Remote notifications: %v", ops) 2847 for _, op := range ops { 2848 cr.log.CDebugf(ctx, "%s: refs %v", op, op.Refs()) 2849 newMD.AddOp(op) 2850 } 2851 2852 // Add a final dummy operation to collect all of the block updates. 2853 newMD.AddOp(newResolutionOp()) 2854 2855 return newMD, nil 2856 } 2857 2858 // resolveOnePath figures out the new merged path, in the resolved 2859 // folder, for a given unmerged pointer. For each node on the path, 2860 // see if the node has been renamed. If so, see if there's a 2861 // resolution for it yet. If there is, complete the path using that 2862 // resolution. If not, recurse. 2863 func (cr *ConflictResolver) resolveOnePath(ctx context.Context, 2864 unmergedMostRecent data.BlockPointer, 2865 unmergedChains, mergedChains, resolvedChains *crChains, 2866 mergedPaths, resolvedPaths map[data.BlockPointer]data.Path) (data.Path, error) { 2867 if p, ok := resolvedPaths[unmergedMostRecent]; ok { 2868 return p, nil 2869 } 2870 2871 // There should always be a merged path, because we should only be 2872 // calling this with pointers that were updated in the unmerged 2873 // branch. 2874 resolvedPath, ok := mergedPaths[unmergedMostRecent] 2875 if !ok { 2876 var ptrsToAppend []data.BlockPointer 2877 var namesToAppend []data.PathPartString 2878 next := unmergedMostRecent 2879 for len(mergedPaths[next].Path) == 0 { 2880 newPtrs := make(map[data.BlockPointer]bool) 2881 ptrs := []data.BlockPointer{unmergedMostRecent} 2882 for ptr := range unmergedChains.byMostRecent { 2883 newPtrs[ptr] = true 2884 } 2885 2886 mdInfo := unmergedChains.mostRecentChainMDInfo 2887 nodeMap, cache, err := cr.fbo.blocks.SearchForNodes( 2888 ctx, cr.fbo.nodeCache, ptrs, newPtrs, 2889 mdInfo, mdInfo.GetRootDirEntry().BlockPointer) 2890 if err != nil { 2891 return data.Path{}, err 2892 } 2893 n := nodeMap[unmergedMostRecent] 2894 if n == nil { 2895 return data.Path{}, fmt.Errorf("resolveOnePath: Couldn't find "+ 2896 "merged path for %v", unmergedMostRecent) 2897 } 2898 p := cache.PathFromNode(n) 2899 ptrsToAppend = append(ptrsToAppend, next) 2900 namesToAppend = append(namesToAppend, p.TailName()) 2901 next = p.ParentPath().TailPointer() 2902 } 2903 resolvedPath = mergedPaths[next] 2904 for i, ptr := range ptrsToAppend { 2905 resolvedPath = resolvedPath.ChildPath( 2906 namesToAppend[i], ptr, cr.fbo.makeObfuscator()) 2907 } 2908 } 2909 2910 i := len(resolvedPath.Path) - 1 2911 for i >= 0 { 2912 mergedMostRecent := resolvedPath.Path[i].BlockPointer 2913 original, err := 2914 mergedChains.originalFromMostRecentOrSame(mergedMostRecent) 2915 if err != nil { 2916 return data.Path{}, err 2917 } 2918 2919 origNewParent, newName, renamed := 2920 resolvedChains.renamedParentAndName(original) 2921 if !renamed { 2922 i-- 2923 continue 2924 } 2925 unmergedNewParent, err := 2926 unmergedChains.mostRecentFromOriginalOrSame(origNewParent) 2927 if err != nil { 2928 return data.Path{}, err 2929 } 2930 2931 // Is the new parent resolved yet? 2932 parentPath, err := cr.resolveOnePath(ctx, unmergedNewParent, 2933 unmergedChains, mergedChains, resolvedChains, mergedPaths, 2934 resolvedPaths) 2935 if err != nil { 2936 return data.Path{}, err 2937 } 2938 2939 // Reset the resolved path 2940 newPathLen := len(parentPath.Path) + len(resolvedPath.Path) - i 2941 newResolvedPath := data.Path{ 2942 FolderBranch: resolvedPath.FolderBranch, 2943 Path: make([]data.PathNode, newPathLen), 2944 ChildObfuscator: cr.fbo.makeObfuscator(), 2945 } 2946 copy(newResolvedPath.Path[:len(parentPath.Path)], parentPath.Path) 2947 copy(newResolvedPath.Path[len(parentPath.Path):], resolvedPath.Path[i:]) 2948 i = len(parentPath.Path) - 1 2949 newNamePPS := data.NewPathPartString( 2950 newName, newResolvedPath.Obfuscator()) 2951 newResolvedPath.Path[i+1].Name = newNamePPS 2952 resolvedPath = newResolvedPath 2953 } 2954 2955 resolvedPaths[unmergedMostRecent] = resolvedPath 2956 return resolvedPath, nil 2957 } 2958 2959 type rootMetadataWithKeyAndTimestamp struct { 2960 *RootMetadata 2961 key kbfscrypto.VerifyingKey 2962 localTimestamp time.Time 2963 } 2964 2965 func (rmd rootMetadataWithKeyAndTimestamp) LastModifyingWriterVerifyingKey() kbfscrypto.VerifyingKey { 2966 return rmd.key 2967 } 2968 2969 func (rmd rootMetadataWithKeyAndTimestamp) LocalTimestamp() time.Time { 2970 return rmd.localTimestamp 2971 } 2972 2973 // makePostResolutionPaths returns the full paths to each unmerged 2974 // pointer, taking into account any rename operations that occurred in 2975 // the merged branch. 2976 func (cr *ConflictResolver) makePostResolutionPaths(ctx context.Context, 2977 md *RootMetadata, unmergedChains, mergedChains *crChains, 2978 mergedPaths map[data.BlockPointer]data.Path) (map[data.BlockPointer]data.Path, error) { 2979 err := cr.checkDone(ctx) 2980 if err != nil { 2981 return nil, err 2982 } 2983 2984 session, err := cr.config.KBPKI().GetCurrentSession(ctx) 2985 if err != nil { 2986 return nil, err 2987 } 2988 2989 // No need to run any identifies on these chains, since we 2990 // have already finished all actions. 2991 resolvedChains, err := newCRChains( 2992 ctx, cr.config.Codec(), cr.config, 2993 []chainMetadata{rootMetadataWithKeyAndTimestamp{md, 2994 session.VerifyingKey, cr.config.Clock().Now()}}, 2995 &cr.fbo.blocks, false) 2996 if err != nil { 2997 return nil, err 2998 } 2999 3000 // If there are no renames, we don't need to fix any of the paths 3001 if len(resolvedChains.renamedOriginals) == 0 { 3002 return mergedPaths, nil 3003 } 3004 3005 resolvedPaths := make(map[data.BlockPointer]data.Path) 3006 for ptr, oldP := range mergedPaths { 3007 p, err := cr.resolveOnePath(ctx, ptr, unmergedChains, mergedChains, 3008 resolvedChains, mergedPaths, resolvedPaths) 3009 if err != nil { 3010 return nil, err 3011 } 3012 cr.log.CDebugf(ctx, "Resolved path for %v from %v to %v", 3013 ptr, oldP.Path, p.Path) 3014 } 3015 3016 return resolvedPaths, nil 3017 } 3018 3019 // getOpsForLocalNotification returns the set of operations that this 3020 // node will need to send local notifications for, in order to 3021 // transition from the staged state to the merged state. 3022 func (cr *ConflictResolver) getOpsForLocalNotification(ctx context.Context, 3023 lState *kbfssync.LockState, md *RootMetadata, 3024 unmergedChains, mergedChains *crChains, 3025 updates map[data.BlockPointer]data.BlockPointer) ( 3026 []op, error) { 3027 dummyOp := newResolutionOp() 3028 newPtrs := make(map[data.BlockPointer]bool) 3029 for mergedMostRecent, newMostRecent := range updates { 3030 // `updates` contains the pointer updates needed for devices 3031 // on the merged branch to update; we have to find the 3032 // original of the entire branch to find the corresponding 3033 // unmerged most recent. 3034 original, err := 3035 mergedChains.originalFromMostRecentOrSame(mergedMostRecent) 3036 if err != nil { 3037 return nil, err 3038 } 3039 chain, ok := unmergedChains.byOriginal[original] 3040 if ok { 3041 // If this unmerged node was updated in the resolution, 3042 // track that update here. 3043 dummyOp.AddUpdate(chain.mostRecent, newMostRecent) 3044 } else { 3045 dummyOp.AddUpdate(original, newMostRecent) 3046 } 3047 newPtrs[newMostRecent] = true 3048 } 3049 3050 var ptrs []data.BlockPointer 3051 chainsToUpdate := make(map[data.BlockPointer]data.BlockPointer) 3052 chainsToAdd := make(map[data.BlockPointer]*crChain) 3053 for ptr, chain := range mergedChains.byMostRecent { 3054 if newMostRecent, ok := updates[chain.original]; ok { 3055 ptrs = append(ptrs, newMostRecent) 3056 chainsToUpdate[chain.mostRecent] = newMostRecent 3057 // This update was already handled above. 3058 continue 3059 } 3060 3061 // If the node changed in both branches, but NOT in the 3062 // resolution, make sure the local notification uses the 3063 // unmerged most recent pointer as the unref. 3064 original := chain.original 3065 if c, ok := unmergedChains.byOriginal[chain.original]; ok { 3066 original = c.mostRecent 3067 updates[chain.original] = chain.mostRecent 3068 3069 // If the node pointer didn't change in the merged chain 3070 // (e.g., due to a setattr), fast forward its most-recent 3071 // pointer to be the unmerged most recent pointer, so that 3072 // local notifications work correctly. 3073 if chain.original == chain.mostRecent { 3074 ptrs = append(ptrs, c.mostRecent) 3075 chainsToAdd[c.mostRecent] = chain 3076 delete(mergedChains.byMostRecent, chain.mostRecent) 3077 chain.mostRecent = c.mostRecent 3078 } 3079 } 3080 3081 newPtrs[ptr] = true 3082 dummyOp.AddUpdate(original, chain.mostRecent) 3083 updates[original] = chain.mostRecent 3084 ptrs = append(ptrs, chain.mostRecent) 3085 } 3086 for ptr, chain := range chainsToAdd { 3087 mergedChains.byMostRecent[ptr] = chain 3088 } 3089 3090 // If any nodes changed only in the unmerged branch, make sure we 3091 // update the pointers in the local ops (e.g., renameOp.Renamed) 3092 // to the latest local most recent. 3093 for original, chain := range unmergedChains.byOriginal { 3094 if _, ok := updates[original]; !ok { 3095 updates[original] = chain.mostRecent 3096 } 3097 } 3098 3099 // Update the merged chains so they all have the new most recent 3100 // pointer. 3101 for mostRecent, newMostRecent := range chainsToUpdate { 3102 chain, ok := mergedChains.byMostRecent[mostRecent] 3103 if !ok { 3104 continue 3105 } 3106 delete(mergedChains.byMostRecent, mostRecent) 3107 chain.mostRecent = newMostRecent 3108 mergedChains.byMostRecent[newMostRecent] = chain 3109 } 3110 3111 // We need to get the complete set of updated merged paths, so 3112 // that we can correctly order the chains from the root outward. 3113 mergedNodeCache := newNodeCacheStandard(cr.fbo.folderBranch) 3114 mergedNodeCache.SetObfuscatorMaker(cr.fbo.makeObfuscator) 3115 nodeMap, _, err := cr.fbo.blocks.SearchForNodes( 3116 ctx, mergedNodeCache, ptrs, newPtrs, 3117 md, md.data.Dir.BlockPointer) 3118 if err != nil { 3119 return nil, err 3120 } 3121 mergedPaths := make([]data.Path, 0, len(nodeMap)) 3122 for _, node := range nodeMap { 3123 if node == nil { 3124 continue 3125 } 3126 mergedPaths = append(mergedPaths, mergedNodeCache.PathFromNode(node)) 3127 } 3128 sort.Sort(crSortedPaths(mergedPaths)) 3129 3130 ops, err := cr.makeRevertedOps( 3131 ctx, lState, mergedPaths, mergedChains, unmergedChains) 3132 if err != nil { 3133 return nil, err 3134 } 3135 newOps, err := fixOpPointersForUpdate(ops, updates, mergedChains) 3136 if err != nil { 3137 return nil, err 3138 } 3139 newOps[0] = dummyOp 3140 return newOps, err 3141 } 3142 3143 // finalizeResolution finishes the resolution process, making the 3144 // resolution visible to any nodes on the merged branch, and taking 3145 // the local node out of staged mode. 3146 func (cr *ConflictResolver) finalizeResolution(ctx context.Context, 3147 lState *kbfssync.LockState, md *RootMetadata, 3148 unmergedChains, mergedChains *crChains, 3149 updates map[data.BlockPointer]data.BlockPointer, 3150 bps blockPutState, blocksToDelete []kbfsblock.ID, writerLocked bool) error { 3151 err := cr.checkDone(ctx) 3152 if err != nil { 3153 return err 3154 } 3155 3156 // Fix up all the block pointers in the merged ops to work well 3157 // for local notifications. Make a dummy op at the beginning to 3158 // convert all the merged most recent pointers into unmerged most 3159 // recent pointers. 3160 newOps, err := cr.getOpsForLocalNotification( 3161 ctx, lState, md, unmergedChains, 3162 mergedChains, updates) 3163 if err != nil { 3164 return err 3165 } 3166 3167 cr.log.CDebugf(ctx, "Local notifications: %v", newOps) 3168 3169 if writerLocked { 3170 return cr.fbo.finalizeResolutionLocked( 3171 ctx, lState, md, bps, newOps, blocksToDelete) 3172 } 3173 return cr.fbo.finalizeResolution( 3174 ctx, lState, md, bps, newOps, blocksToDelete) 3175 } 3176 3177 // completeResolution pushes all the resolved blocks to the servers, 3178 // computes all remote and local notifications, and finalizes the 3179 // resolution process. 3180 func (cr *ConflictResolver) completeResolution(ctx context.Context, 3181 lState *kbfssync.LockState, unmergedChains, mergedChains *crChains, 3182 unmergedPaths []data.Path, mergedPaths map[data.BlockPointer]data.Path, 3183 mostRecentUnmergedMD, mostRecentMergedMD ImmutableRootMetadata, 3184 dbm dirBlockMap, newFileBlocks fileBlockMap, 3185 dirtyBcache data.DirtyBlockCacheSimple, bps blockPutState, 3186 writerLocked bool) (err error) { 3187 md, err := cr.createResolvedMD( 3188 ctx, lState, unmergedPaths, unmergedChains, 3189 mergedChains, mostRecentMergedMD) 3190 if err != nil { 3191 return err 3192 } 3193 3194 resolvedPaths, err := cr.makePostResolutionPaths(ctx, md, unmergedChains, 3195 mergedChains, mergedPaths) 3196 if err != nil { 3197 return err 3198 } 3199 3200 err = cr.checkDone(ctx) 3201 if err != nil { 3202 return err 3203 } 3204 3205 // Find any paths that don't have any ops associated with them, 3206 // and avoid making new blocks for them in the resolution. 3207 // Without this, we will end up with an extraneous block update 3208 // for the directory with no ops. Then, if this resolution ends 3209 // up going through ANOTHER resolution later, which sees no ops 3210 // need resolving and short-circuits the resolution process, we 3211 // could end up accidentally unreferencing a merged directory 3212 // block that's still in use. See KBFS-2825 for details. 3213 hasChildOps := make(map[data.BlockPointer]bool) 3214 for _, p := range unmergedPaths { 3215 chain := unmergedChains.byMostRecent[p.TailPointer()] 3216 if len(chain.ops) == 0 { 3217 continue 3218 } 3219 for _, pn := range p.Path { 3220 hasChildOps[pn.BlockPointer] = true 3221 } 3222 } 3223 for ptr := range resolvedPaths { 3224 if !hasChildOps[ptr] { 3225 cr.log.CDebugf(ctx, 3226 "Removing resolved path for op-less unmerged block pointer %v", 3227 ptr) 3228 delete(resolvedPaths, ptr) 3229 } 3230 } 3231 3232 updates, blocksToDelete, err := cr.prepper.prepUpdateForPaths( 3233 ctx, lState, md, unmergedChains, mergedChains, 3234 mostRecentUnmergedMD, mostRecentMergedMD, resolvedPaths, dbm, 3235 newFileBlocks, dirtyBcache, bps, prepFolderCopyIndirectFileBlocks) 3236 if err != nil { 3237 return err 3238 } 3239 3240 // Can only do this after prepUpdateForPaths, since 3241 // prepUpdateForPaths calls fixOpPointersForUpdate, and the ops 3242 // may be invalid until then. 3243 err = md.data.checkValid() 3244 if err != nil { 3245 return err 3246 } 3247 3248 defer func() { 3249 if err != nil { 3250 cr.fbo.fbm.cleanUpBlockState( 3251 md.ReadOnly(), bps, blockDeleteOnMDFail) 3252 } 3253 }() 3254 3255 err = cr.checkDone(ctx) 3256 if err != nil { 3257 return err 3258 } 3259 3260 // Put all the blocks. TODO: deal with recoverable block errors? 3261 cacheType := DiskBlockAnyCache 3262 if cr.config.IsSyncedTlf(md.TlfID()) { 3263 cacheType = DiskBlockSyncCache 3264 } 3265 _, err = doBlockPuts( 3266 ctx, cr.config.BlockServer(), cr.config.BlockCache(), 3267 cr.config.Reporter(), cr.log, cr.deferLog, md.TlfID(), 3268 md.GetTlfHandle().GetCanonicalName(), bps, cacheType) 3269 if err != nil { 3270 return err 3271 } 3272 3273 err = cr.finalizeResolution(ctx, lState, md, unmergedChains, 3274 mergedChains, updates, bps, blocksToDelete, writerLocked) 3275 if err != nil { 3276 return err 3277 } 3278 return nil 3279 } 3280 3281 const conflictRecordVersion = 1 3282 3283 type conflictRecord struct { 3284 Version int `json:"-"` 3285 Time time.Time 3286 Merged string 3287 Unmerged string 3288 ErrorTime time.Time 3289 ErrorString string 3290 PanicString string 3291 codec.UnknownFieldSetHandler `json:"-"` 3292 } 3293 3294 func getAndDeserializeConflicts(config Config, db *ldbutils.LevelDb, 3295 key []byte) ([]conflictRecord, error) { 3296 if db == nil { 3297 return nil, errors.New("No conflict DB given") 3298 } 3299 conflictsSoFarSerialized, err := db.Get(key, nil) 3300 var conflictsSoFar []conflictRecord 3301 switch errors.Cause(err) { 3302 case leveldb.ErrNotFound: 3303 conflictsSoFar = nil 3304 case nil: 3305 err = config.Codec().Decode(conflictsSoFarSerialized, &conflictsSoFar) 3306 if err != nil { 3307 return nil, err 3308 } 3309 default: 3310 return nil, err 3311 } 3312 return conflictsSoFar, nil 3313 } 3314 3315 func serializeAndPutConflicts(config Config, db *ldbutils.LevelDb, 3316 key []byte, conflicts []conflictRecord) error { 3317 if db == nil { 3318 return errors.New("No conflict DB given") 3319 } 3320 3321 conflictsSerialized, err := config.Codec().Encode(conflicts) 3322 if err != nil { 3323 return err 3324 } 3325 return db.Put(key, conflictsSerialized, nil) 3326 } 3327 3328 func isCRStuckFromRecords(conflictsSoFar []conflictRecord) bool { 3329 // If we're exactly at the threshold, make sure the last attempt 3330 // has completed. 3331 if len(conflictsSoFar) == maxConflictResolutionAttempts+1 { 3332 return !conflictsSoFar[len(conflictsSoFar)-1].ErrorTime.IsZero() 3333 } 3334 return len(conflictsSoFar) > maxConflictResolutionAttempts 3335 } 3336 3337 func (cr *ConflictResolver) isStuckWithDbAndConflicts() ( 3338 db *ldbutils.LevelDb, key []byte, conflictsSoFar []conflictRecord, 3339 isStuck bool, err error) { 3340 db = cr.config.GetConflictResolutionDB() 3341 if db == nil { 3342 return nil, nil, nil, false, errNoCRDB 3343 } 3344 key = cr.fbo.id().Bytes() 3345 conflictsSoFar, err = getAndDeserializeConflicts(cr.config, db, key) 3346 if err != nil { 3347 return nil, nil, nil, false, err 3348 } 3349 3350 return db, key, conflictsSoFar, isCRStuckFromRecords(conflictsSoFar), nil 3351 } 3352 3353 func (cr *ConflictResolver) isStuck() (bool, error) { 3354 _, _, _, isStuck, err := cr.isStuckWithDbAndConflicts() 3355 return isStuck, err 3356 } 3357 3358 func (cr *ConflictResolver) recordStartResolve(ci conflictInput) error { 3359 db, key, conflictsSoFar, isStuck, err := cr.isStuckWithDbAndConflicts() 3360 if err != nil { 3361 return err 3362 } 3363 if isStuck { 3364 return ErrTooManyCRAttempts 3365 } 3366 conflictsSoFar = append(conflictsSoFar, conflictRecord{ 3367 Version: conflictRecordVersion, 3368 Time: cr.config.Clock().Now(), 3369 Merged: ci.merged.String(), 3370 Unmerged: ci.unmerged.String(), 3371 }) 3372 return serializeAndPutConflicts(cr.config, db, key, conflictsSoFar) 3373 } 3374 3375 // recordFinishResolve does one of two things: 3376 // - in the event of success, it deletes the DB entry that recorded conflict 3377 // resolution attempts for this resolver 3378 // - in the event of failure, it logs that CR failed and tries to record the 3379 // failure to the DB. 3380 func (cr *ConflictResolver) recordFinishResolve( 3381 ctx context.Context, ci conflictInput, 3382 panicVar interface{}, receivedErr error) { 3383 db, key, _, wasStuck, err := cr.isStuckWithDbAndConflicts() 3384 if err != nil { 3385 cr.log.CWarningf(ctx, "could not record CR result: %+v", err) 3386 return 3387 } 3388 3389 // If we neither errored nor panicked, this CR succeeded and we can wipe 3390 // the DB entry. 3391 if (receivedErr == nil || receivedErr == context.Canceled) && 3392 panicVar == nil { 3393 err := db.Delete(key, nil) 3394 if err != nil { 3395 cr.log.CWarningf(ctx, 3396 "Could not record conflict resolution success: %v", err) 3397 } 3398 3399 if wasStuck { 3400 cr.config.SubscriptionManagerPublisher().PublishChange(keybase1.SubscriptionTopic_FAVORITES) 3401 cr.config.Reporter().NotifyFavoritesChanged(ctx) 3402 cr.config.SubscriptionManagerPublisher().PublishChange( 3403 keybase1.SubscriptionTopic_FILES_TAB_BADGE) 3404 } 3405 return 3406 } 3407 3408 defer func() { 3409 // If we can't record the failure to the CR DB, at least log it. 3410 if err != nil { 3411 cr.log.CWarningf(ctx, 3412 "Could not record conflict resolution failure [%v/%v]: %v", 3413 receivedErr, panicVar, err) 3414 } 3415 // If we recovered from a panic, keep panicking. 3416 if panicVar != nil { 3417 panic(panicVar) 3418 } 3419 }() 3420 3421 // Otherwise we need to decode the most recent entry, modify it, and put it 3422 // back in the DB. 3423 var conflictsSoFar []conflictRecord 3424 conflictsSoFar, err = getAndDeserializeConflicts(cr.config, db, key) 3425 if err != nil { 3426 return 3427 } 3428 3429 thisCR := &conflictsSoFar[len(conflictsSoFar)-1] 3430 thisCR.ErrorTime = cr.config.Clock().Now() 3431 if receivedErr != nil { 3432 thisCR.ErrorString = fmt.Sprintf("%+v", receivedErr) 3433 } 3434 if panicVar != nil { 3435 thisCR.PanicString = fmt.Sprintf("panic(%s). stack: %s", panicVar, 3436 debug.Stack()) 3437 } 3438 3439 err = serializeAndPutConflicts(cr.config, db, key, conflictsSoFar) 3440 if err != nil { 3441 cr.log.CWarningf(ctx, 3442 "Could not record conflict resolution success: %+v", err) 3443 return 3444 } 3445 3446 if !wasStuck && isCRStuckFromRecords(conflictsSoFar) { 3447 cr.config.SubscriptionManagerPublisher().PublishChange(keybase1.SubscriptionTopic_FAVORITES) 3448 cr.config.Reporter().NotifyFavoritesChanged(ctx) 3449 cr.config.SubscriptionManagerPublisher().PublishChange( 3450 keybase1.SubscriptionTopic_FILES_TAB_BADGE) 3451 cr.config.GetPerfLog().CDebugf( 3452 ctx, "Conflict resolution failed too many times for %s", 3453 cr.fbo.id()) 3454 } 3455 } 3456 3457 func (cr *ConflictResolver) makeDiskBlockCache(ctx context.Context) ( 3458 dbc *DiskBlockCacheLocal, cleanupFn func(context.Context), err error) { 3459 if cr.config.IsTestMode() { 3460 // Enable the disk limiter if one doesn't exist yet. 3461 _ = cr.config.(*ConfigLocal).EnableDiskLimiter(os.TempDir()) 3462 3463 dbc, err = newDiskBlockCacheLocalForTest( 3464 cr.config, crDirtyBlockCacheLimitTrackerType) 3465 if err != nil { 3466 return nil, nil, err 3467 } 3468 cleanupFn = func(ctx context.Context) { 3469 <-dbc.Shutdown(ctx) 3470 } 3471 } else { 3472 tempDir, err := os.MkdirTemp( 3473 cr.config.StorageRoot(), ConflictStorageRootPrefix) 3474 if err != nil { 3475 return nil, nil, err 3476 } 3477 dirCleanupFn := func(_ context.Context) { 3478 err := os.RemoveAll(tempDir) 3479 if err != nil { 3480 cr.log.CDebugf(ctx, "Error cleaning up tempdir %s: %+v", 3481 tempDir, err) 3482 } 3483 } 3484 dbc, err = newDiskBlockCacheLocal( 3485 cr.config, crDirtyBlockCacheLimitTrackerType, tempDir, cr.config.Mode()) 3486 if err != nil { 3487 dirCleanupFn(ctx) 3488 return nil, nil, err 3489 } 3490 cleanupFn = func(ctx context.Context) { 3491 dbc.Shutdown(ctx) 3492 dirCleanupFn(ctx) 3493 } 3494 } 3495 3496 err = dbc.WaitUntilStarted() 3497 if err != nil { 3498 if cleanupFn != nil { 3499 cleanupFn(ctx) 3500 } 3501 return nil, nil, err 3502 } 3503 3504 return dbc, cleanupFn, nil 3505 } 3506 3507 func (cr *ConflictResolver) getFailModeForTesting() failModeForTesting { 3508 cr.failModeLock.RLock() 3509 defer cr.failModeLock.RUnlock() 3510 return cr.failModeForTesting 3511 } 3512 3513 func (cr *ConflictResolver) setFailModeForTesting(mode failModeForTesting) { 3514 cr.failModeLock.Lock() 3515 defer cr.failModeLock.Unlock() 3516 cr.failModeForTesting = mode 3517 } 3518 3519 // CRWrapError wraps an error that happens during conflict resolution. 3520 type CRWrapError struct { 3521 err error 3522 } 3523 3524 // Error implements the error interface for CRWrapError. 3525 func (e CRWrapError) Error() string { 3526 return "Conflict resolution error: " + e.err.Error() 3527 } 3528 3529 func (cr *ConflictResolver) doResolve(ctx context.Context, ci conflictInput) { 3530 var err error 3531 ctx = cr.config.MaybeStartTrace(ctx, "CR.doResolve", 3532 fmt.Sprintf("%s %+v", cr.fbo.folderBranch, ci)) 3533 defer func() { cr.config.MaybeFinishTrace(ctx, err) }() 3534 3535 err = cr.recordStartResolve(ci) 3536 switch errors.Cause(err) { 3537 case ErrTooManyCRAttempts: 3538 cr.log.CWarningf(ctx, 3539 "Too many failed CR attempts for folder: %v", cr.fbo.id()) 3540 cr.config.GetPerfLog().CDebugf( 3541 ctx, "Conflict resolution failed too many times for %v", err) 3542 return 3543 case nil: 3544 defer func() { 3545 r := recover() 3546 cr.recordFinishResolve(ctx, ci, r, err) 3547 }() 3548 default: 3549 cr.log.CWarningf(ctx, 3550 "Could not record conflict resolution attempt: %+v", err) 3551 } 3552 3553 cr.log.CDebugf(ctx, "Starting conflict resolution with input %+v", ci) 3554 lState := makeFBOLockState() 3555 defer func() { 3556 cr.deferLog.CDebugf(ctx, "Finished conflict resolution: %+v", err) 3557 if err != nil { 3558 head := cr.fbo.getTrustedHead(ctx, lState, mdNoCommit) 3559 if head == (ImmutableRootMetadata{}) { 3560 panic("doResolve: head is nil (should be impossible)") 3561 } 3562 handle := head.GetTlfHandle() 3563 cr.config.Reporter().ReportErr( 3564 ctx, handle.GetCanonicalName(), handle.Type(), 3565 WriteMode, CRWrapError{err}) 3566 if err == context.Canceled { 3567 cr.inputLock.Lock() 3568 defer cr.inputLock.Unlock() 3569 cr.canceledCount++ 3570 // TODO: decrease threshold for pending local squashes? 3571 if cr.canceledCount > cr.maxRevsThreshold { 3572 cr.lockNextTime = true 3573 } 3574 } 3575 } else { 3576 // We finished successfully, so no need to lock next time. 3577 cr.inputLock.Lock() 3578 defer cr.inputLock.Unlock() 3579 cr.lockNextTime = false 3580 cr.canceledCount = 0 3581 } 3582 }() 3583 3584 // Canceled before we even got started? 3585 err = cr.checkDone(ctx) 3586 if err != nil { 3587 return 3588 } 3589 3590 if cr.getFailModeForTesting() == alwaysFailCR { 3591 err = ErrCRFailForTesting 3592 return 3593 } 3594 3595 var mergedMDs []ImmutableRootMetadata 3596 3597 // Check if we need to deploy the nuclear option and completely 3598 // block unmerged writes while we try to resolve. 3599 doLock := func() bool { 3600 cr.inputLock.Lock() 3601 defer cr.inputLock.Unlock() 3602 return cr.lockNextTime 3603 }() 3604 if doLock { 3605 cr.log.CDebugf(ctx, "Blocking unmerged writes due to large amounts "+ 3606 "of unresolved state") 3607 cr.fbo.blockUnmergedWrites(lState) 3608 defer cr.fbo.unblockUnmergedWrites(lState) 3609 err = cr.checkDone(ctx) 3610 if err != nil { 3611 return 3612 } 3613 3614 // Sync everything from memory to the journal. 3615 err = cr.fbo.syncAllLocked(ctx, lState, NoExcl) 3616 if err != nil { 3617 return 3618 } 3619 3620 // Don't let us hold the lock for too long though 3621 var cancel context.CancelFunc 3622 ctx, cancel = context.WithTimeout(ctx, crMaxWriteLockTime) 3623 defer cancel() 3624 cr.log.CDebugf(ctx, "Unmerged writes blocked") 3625 } else { 3626 // Sync everything from memory to the journal. 3627 err = cr.fbo.syncAllUnlocked(ctx, lState) 3628 if err != nil { 3629 return 3630 } 3631 } 3632 3633 // Step 1: Build the chains for each branch, as well as the paths 3634 // and necessary extra recreate ops. The result of this step is: 3635 // * A set of conflict resolution "chains" for both the unmerged and 3636 // merged branches 3637 // * A map containing, for each changed unmerged node, the full path to 3638 // the corresponding merged node. 3639 // * A set of "recreate" ops that must be applied on the merged branch 3640 // to recreate any directories that were modified in the unmerged 3641 // branch but removed in the merged branch. 3642 unmergedChains, mergedChains, unmergedPaths, mergedPaths, recOps, 3643 unmergedMDs, mergedMDs, err := 3644 cr.buildChainsAndPaths(ctx, lState, doLock) 3645 if err != nil { 3646 return 3647 } 3648 if len(unmergedMDs) == 0 { 3649 // TODO: This is probably due to an extra Resolve() call that 3650 // got queued during a resolution (but too late to cancel it), 3651 // and executed after the resolution completed successfully. 3652 cr.log.CDebugf(ctx, "No unmerged updates at all, so we must not be "+ 3653 "unmerged after all") 3654 return 3655 } 3656 if len(mergedPaths) == 0 || len(mergedMDs) == 0 { 3657 var mostRecentMergedMD ImmutableRootMetadata 3658 if len(mergedMDs) > 0 { 3659 mostRecentMergedMD = mergedMDs[len(mergedMDs)-1] 3660 } else { 3661 branchPoint := unmergedMDs[0].Revision() - 1 3662 mostRecentMergedMD, err = GetSingleMD(ctx, cr.config, cr.fbo.id(), 3663 kbfsmd.NullBranchID, branchPoint, kbfsmd.Merged, nil) 3664 if err != nil { 3665 return 3666 } 3667 } 3668 // TODO: All the other variables returned by 3669 // buildChainsAndPaths may also be nil, in which case 3670 // completeResolution will deref a nil pointer. Fix 3671 // this! 3672 // 3673 // nothing to do 3674 cr.log.CDebugf(ctx, "No updates to resolve, so finishing") 3675 dbm := newDirBlockMapMemory() 3676 newFileBlocks := newFileBlockMapMemory() 3677 bps := newBlockPutStateMemory(0) 3678 err = cr.completeResolution(ctx, lState, unmergedChains, 3679 mergedChains, unmergedPaths, mergedPaths, 3680 unmergedMDs[len(unmergedMDs)-1], mostRecentMergedMD, dbm, 3681 newFileBlocks, nil, bps, doLock) 3682 return 3683 } 3684 3685 err = cr.checkDone(ctx) 3686 if err != nil { 3687 return 3688 } 3689 3690 if status, _, err := cr.fbo.status.getStatus(ctx, nil); err == nil { 3691 if statusString, err := json.Marshal(status); err == nil { 3692 ci := func() conflictInput { 3693 cr.inputLock.Lock() 3694 defer cr.inputLock.Unlock() 3695 return cr.currInput 3696 }() 3697 cr.log.CInfof(ctx, "Current status during conflict resolution "+ 3698 "(input %v): %s", ci, statusString) 3699 } 3700 } 3701 cr.log.CDebugf(ctx, "Recreate ops: %s", recOps) 3702 3703 mostRecentMergedMD := mergedMDs[len(mergedMDs)-1] 3704 3705 mostRecentMergedWriterInfo := newWriterInfo( 3706 mostRecentMergedMD.LastModifyingWriter(), 3707 mostRecentMergedMD.LastModifyingWriterVerifyingKey(), 3708 mostRecentMergedMD.Revision(), cr.fbo.oa()) 3709 3710 // Step 2: Figure out which actions need to be taken in the merged 3711 // branch to best reflect the unmerged changes. The result of 3712 // this step is a map containing, for each node in the merged path 3713 // that will be updated during conflict resolution, a set of 3714 // "actions" to be applied to the merged branch. Each of these 3715 // actions contains the logic needed to manipulate the data into 3716 // the final merged state, including the resolution of any 3717 // conflicts that occurred between the two branches. 3718 actionMap, newUnmergedPaths, err := cr.computeActions( 3719 ctx, unmergedChains, mergedChains, unmergedPaths, mergedPaths, 3720 recOps, mostRecentMergedWriterInfo) 3721 if err != nil { 3722 return 3723 } 3724 3725 // Insert the new unmerged paths as needed 3726 if len(newUnmergedPaths) > 0 { 3727 unmergedPaths = append(unmergedPaths, newUnmergedPaths...) 3728 sort.Sort(crSortedPaths(unmergedPaths)) 3729 } 3730 3731 err = cr.checkDone(ctx) 3732 if err != nil { 3733 return 3734 } 3735 3736 cr.log.CDebugf(ctx, "Action map: %v", actionMap) 3737 3738 // Step 3: Apply the actions by looking up the corresponding 3739 // unmerged dir entry and copying it to a copy of the 3740 // corresponding merged block. Keep these dirty block copies in a 3741 // local dirty cache, keyed by corresponding merged most recent 3742 // pointer. 3743 // 3744 // At the same time, construct two sets of ops: one that will be 3745 // put into the final MD object that gets merged, and one that 3746 // needs to be played through as notifications locally to get any 3747 // local caches synchronized with the final merged state. 3748 // 3749 // * This will be taken care of by each crAction.updateOps() 3750 // method, which modifies the unmerged and merged ops for a 3751 // particular chain. After all the crActions are applied, the 3752 // "unmerged" ops need to be pushed as part of the MD update, 3753 // while the "merged" ops need to be applied locally. 3754 3755 // newFileBlocks contains the copies of the file blocks we need to 3756 // sync. If a block is indirect, we need to put it and add new 3757 // references for all indirect pointers inside it. If it is not 3758 // an indirect block, just add a new reference to the block. 3759 dbc, cleanupFn, err := cr.makeDiskBlockCache(ctx) 3760 if err != nil { 3761 return 3762 } 3763 if cleanupFn != nil { 3764 defer cleanupFn(ctx) 3765 } 3766 dirtyBcache := newDirtyBlockCacheDisk( 3767 cr.config, dbc, mergedChains.mostRecentChainMDInfo, cr.fbo.branch()) 3768 newFileBlocks := newFileBlockMapDisk( 3769 dirtyBcache, mergedChains.mostRecentChainMDInfo) 3770 // dbm contains the modified directory blocks we need to sync 3771 dbm := newDirBlockMapDisk(dirtyBcache, mergedChains.mostRecentChainMDInfo) 3772 3773 err = cr.doActions(ctx, lState, unmergedChains, mergedChains, 3774 unmergedPaths, mergedPaths, actionMap, dbm, newFileBlocks, dirtyBcache) 3775 if err != nil { 3776 return 3777 } 3778 3779 err = cr.checkDone(ctx) 3780 if err != nil { 3781 return 3782 } 3783 cr.log.CDebugf(ctx, "Executed all actions, %d updated directory blocks", 3784 dbm.numBlocks()) 3785 3786 // Step 4: finish up by syncing all the blocks, computing and 3787 // putting the final resolved MD, and issuing all the local 3788 // notifications. 3789 bps := newBlockPutStateDisk( 3790 0, cr.config, dbc, mergedChains.mostRecentChainMDInfo) 3791 err = cr.completeResolution(ctx, lState, unmergedChains, mergedChains, 3792 unmergedPaths, mergedPaths, unmergedMDs[len(unmergedMDs)-1], 3793 mostRecentMergedMD, dbm, newFileBlocks, dirtyBcache, bps, doLock) 3794 if err != nil { 3795 return 3796 } 3797 3798 // TODO: If conflict resolution fails after some blocks were put, 3799 // remember these and include them in the later resolution so they 3800 // don't count against the quota forever. (Though of course if we 3801 // completely fail, we'll need to rely on a future complete scan 3802 // to clean up the quota anyway . . .) 3803 } 3804 3805 func (cr *ConflictResolver) clearConflictRecords(ctx context.Context) error { 3806 db, key, _, wasStuck, err := cr.isStuckWithDbAndConflicts() 3807 if err != nil { 3808 return err 3809 } 3810 3811 err = db.Delete(key, nil) 3812 if err != nil { 3813 return err 3814 } 3815 3816 if wasStuck { 3817 cr.config.SubscriptionManagerPublisher().PublishChange(keybase1.SubscriptionTopic_FAVORITES) 3818 cr.config.Reporter().NotifyFavoritesChanged(ctx) 3819 cr.config.SubscriptionManagerPublisher().PublishChange( 3820 keybase1.SubscriptionTopic_FILES_TAB_BADGE) 3821 } 3822 return nil 3823 } 3824 3825 func openCRDBInternal(config Config) (*ldbutils.LevelDb, error) { 3826 if config.IsTestMode() { 3827 return ldbutils.OpenLevelDb(storage.NewMemStorage(), config.Mode()) 3828 } 3829 err := os.MkdirAll(sysPath.Join(config.StorageRoot(), 3830 conflictResolverRecordsDir, conflictResolverRecordsVersionString), 3831 os.ModePerm) 3832 if err != nil { 3833 return nil, err 3834 } 3835 3836 stor, err := storage.OpenFile(sysPath.Join(config.StorageRoot(), 3837 conflictResolverRecordsDir, conflictResolverRecordsVersionString, 3838 conflictResolverRecordsDB), false) 3839 3840 if err != nil { 3841 return nil, err 3842 } 3843 3844 return ldbutils.OpenLevelDb(stor, config.Mode()) 3845 } 3846 3847 func openCRDB(config Config) (db *ldbutils.LevelDb) { 3848 db, err := openCRDBInternal(config) 3849 if err != nil { 3850 config.MakeLogger("").CWarningf(context.Background(), 3851 "Could not open conflict resolver DB. "+ 3852 "Perhaps multiple KBFS instances are being run concurrently"+ 3853 "? Error: %+v", err) 3854 } 3855 return db 3856 }