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  }