github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/cr_chains.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  	"time"
    11  
    12  	"github.com/keybase/client/go/kbfs/data"
    13  	"github.com/keybase/client/go/kbfs/idutil"
    14  	"github.com/keybase/client/go/kbfs/kbfscodec"
    15  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    16  	"github.com/keybase/client/go/kbfs/kbfsmd"
    17  	"github.com/keybase/client/go/kbfs/libkey"
    18  	"github.com/keybase/client/go/kbfs/tlf"
    19  	"github.com/keybase/client/go/logger"
    20  	"github.com/keybase/client/go/protocol/keybase1"
    21  	"github.com/pkg/errors"
    22  	"golang.org/x/net/context"
    23  )
    24  
    25  // crChain represents the set of operations that happened to a
    26  // particular KBFS node (e.g., individual file or directory) over a
    27  // given set of MD updates.  It also tracks the starting and ending
    28  // block pointers for the node.
    29  type crChain struct {
    30  	ops                  []op
    31  	original, mostRecent data.BlockPointer
    32  	file                 bool
    33  	obfuscator           data.Obfuscator
    34  	tlfID                tlf.ID
    35  }
    36  
    37  // collapse finds complementary pairs of operations that cancel each
    38  // other out, and remove the relevant operations from the chain.
    39  // Examples include:
    40  //   - A create followed by a remove for the same name (delete both ops)
    41  //   - A create followed by a create (renamed == true) for the same name
    42  //     (delete the create op)
    43  //   - A remove that only unreferences blocks created within this branch
    44  //
    45  // This function returns the list of pointers that should be unreferenced
    46  // as part of an eventual resolution of the corresponding branch.
    47  func (cc *crChain) collapse(createdOriginals map[data.BlockPointer]bool,
    48  	originals map[data.BlockPointer]data.BlockPointer) (toUnrefs []data.BlockPointer) {
    49  	createsSeen := make(map[string]int)
    50  	indicesToRemove := make(map[int]bool)
    51  	var wr []WriteRange
    52  	lastSyncOp := -1
    53  	var syncRefs, syncUnrefs []data.BlockPointer
    54  	for i, op := range cc.ops {
    55  		switch realOp := op.(type) {
    56  		case *createOp:
    57  			if prevCreateIndex, ok :=
    58  				createsSeen[realOp.NewName]; realOp.renamed && ok {
    59  				// A rename has papered over the first create, so
    60  				// just drop it.
    61  				indicesToRemove[prevCreateIndex] = true
    62  			}
    63  			createsSeen[realOp.NewName] = i
    64  		case *rmOp:
    65  			if prevCreateIndex, ok := createsSeen[realOp.OldName]; ok {
    66  				delete(createsSeen, realOp.OldName)
    67  				// The rm cancels out the create, so remove it.
    68  				indicesToRemove[prevCreateIndex] = true
    69  				// Also remove the rmOp if it was part of a rename
    70  				// (i.e., it wasn't a "real" rm), or if it otherwise
    71  				// only unreferenced blocks that were created on this
    72  				// branch.
    73  				doRemove := true
    74  				for _, unref := range op.Unrefs() {
    75  					original, ok := originals[unref]
    76  					if !ok {
    77  						original = unref
    78  					}
    79  					if !createdOriginals[original] {
    80  						doRemove = false
    81  						break
    82  					}
    83  				}
    84  				if doRemove {
    85  					indicesToRemove[i] = true
    86  					toUnrefs = append(toUnrefs, op.Unrefs()...)
    87  				}
    88  			}
    89  		case *setAttrOp:
    90  			// TODO: Collapse opposite setex pairs
    91  		case *syncOp:
    92  			wr = realOp.collapseWriteRange(wr)
    93  			indicesToRemove[i] = true
    94  			lastSyncOp = i
    95  			// The last op will have its refs listed twice in the
    96  			// collapsed op, but that's harmless.
    97  			syncRefs = append(syncRefs, op.Refs()...)
    98  			// The last op will have its unrefs listed twice in the
    99  			// collapsed op, but that's harmless.
   100  			syncUnrefs = append(syncUnrefs, op.Unrefs()...)
   101  		default:
   102  			// ignore other op types
   103  		}
   104  	}
   105  
   106  	if len(indicesToRemove) > 0 {
   107  		ops := make([]op, 0, len(cc.ops)-len(indicesToRemove))
   108  		for i, op := range cc.ops {
   109  			if i == lastSyncOp {
   110  				so, ok := op.(*syncOp)
   111  				if !ok {
   112  					panic(fmt.Sprintf(
   113  						"Op %s at index %d should have been a syncOp", op, i))
   114  				}
   115  				so.Writes = wr
   116  				for _, ref := range syncRefs {
   117  					op.AddRefBlock(ref)
   118  				}
   119  				for _, unref := range syncUnrefs {
   120  					op.AddUnrefBlock(unref)
   121  				}
   122  				ops = append(ops, op)
   123  			} else if !indicesToRemove[i] {
   124  				ops = append(ops, op)
   125  			}
   126  		}
   127  		cc.ops = ops
   128  	}
   129  	return toUnrefs
   130  }
   131  
   132  func (cc *crChain) getCollapsedWriteRange() []WriteRange {
   133  	if !cc.isFile() {
   134  		return nil
   135  	}
   136  	var wr []WriteRange
   137  	for _, op := range cc.ops {
   138  		syncOp, ok := op.(*syncOp)
   139  		if !ok {
   140  			continue
   141  		}
   142  		wr = syncOp.collapseWriteRange(wr)
   143  	}
   144  	return wr
   145  }
   146  
   147  func writeRangesEquivalent(
   148  	wr1 []WriteRange, wr2 []WriteRange) bool {
   149  	// Both empty?
   150  	if len(wr1) == 0 && len(wr2) == 0 {
   151  		return true
   152  	}
   153  
   154  	// If both branches contain no writes, and their truncation
   155  	// points are the same, then there are no unmerged actions to
   156  	// take.
   157  	if len(wr1) == 1 && wr1[0].isTruncate() &&
   158  		len(wr2) == 1 && wr2[0].isTruncate() &&
   159  		wr1[0].Off == wr2[0].Off {
   160  		return true
   161  	}
   162  
   163  	// TODO: In the future we may be able to do smarter merging
   164  	// here if the write ranges don't overlap, though maybe only
   165  	// for certain file types?
   166  	return false
   167  }
   168  
   169  func (cc *crChain) removeSyncOps() {
   170  	var newOps []op
   171  	for _, op := range cc.ops {
   172  		if _, ok := op.(*syncOp); !ok {
   173  			newOps = append(newOps, op)
   174  		}
   175  	}
   176  
   177  	cc.ops = newOps
   178  }
   179  
   180  func (cc *crChain) getActionsToMerge(
   181  	ctx context.Context, renamer ConflictRenamer, mergedPath data.Path,
   182  	mergedChain *crChain) (crActionList, error) {
   183  	var actions crActionList
   184  
   185  	// If this is a file, determine whether the unmerged chain
   186  	// could actually have changed the file in some way that it
   187  	// hasn't already been changed.  For example, if they both
   188  	// truncate the file to the same length, and there are no
   189  	// other writes, we can just drop the unmerged syncs.
   190  	if cc.isFile() && mergedChain != nil {
   191  		// The write ranges should already be collapsed into a single
   192  		// syncOp, these calls just find that one remaining syncOp.
   193  		myWriteRange := cc.getCollapsedWriteRange()
   194  		mergedWriteRange := mergedChain.getCollapsedWriteRange()
   195  
   196  		if writeRangesEquivalent(myWriteRange, mergedWriteRange) {
   197  			// drop all sync ops
   198  			cc.removeSyncOps()
   199  		}
   200  	}
   201  
   202  	// Check each op against all ops in the corresponding merged
   203  	// chain, looking for conflicts.  If there is a conflict, return
   204  	// it as part of the action list.  If there are no conflicts for
   205  	// that op, return the op's default actions.
   206  	for _, unmergedOp := range cc.ops {
   207  		conflict := false
   208  		if mergedChain != nil {
   209  			for _, mergedOp := range mergedChain.ops {
   210  				action, err :=
   211  					unmergedOp.checkConflict(
   212  						ctx, renamer, mergedOp, cc.isFile())
   213  				if err != nil {
   214  					return nil, err
   215  				}
   216  				if action != nil {
   217  					conflict = true
   218  					actions = append(actions, action)
   219  				}
   220  			}
   221  		}
   222  		// no conflicts!
   223  		if !conflict {
   224  			action := unmergedOp.getDefaultAction(mergedPath)
   225  			if action != nil {
   226  				actions = append(actions, action)
   227  			}
   228  		}
   229  	}
   230  
   231  	return actions, nil
   232  }
   233  
   234  func (cc *crChain) isFile() bool {
   235  	return cc.file
   236  }
   237  
   238  // identifyType figures out whether this chain represents a file or
   239  // directory.  It tries to figure it out based purely on operation
   240  // state, but setAttr(mtime) can apply to either type; in that case,
   241  // we need to fetch the block to figure out the type.
   242  func (cc *crChain) identifyType(ctx context.Context, fbo *folderBlockOps,
   243  	kmd libkey.KeyMetadata, chains *crChains) error {
   244  	if len(cc.ops) == 0 {
   245  		return nil
   246  	}
   247  
   248  	// If any op is setAttr (ex or size) or sync, this is a file
   249  	// chain.  If it only has a setAttr/mtime, we don't know what it
   250  	// is, so fall through and fetch the block unless we come across
   251  	// another op that can determine the type.
   252  	var parentDir data.BlockPointer
   253  	var lastSetAttr *setAttrOp
   254  	for _, op := range cc.ops {
   255  		switch realOp := op.(type) {
   256  		case *syncOp:
   257  			cc.file = true
   258  			return nil
   259  		case *setAttrOp:
   260  			if realOp.Attr != mtimeAttr {
   261  				cc.file = true
   262  				return nil
   263  			}
   264  			// We can't tell the file type from an mtimeAttr, so we
   265  			// may have to actually fetch the block to figure it out.
   266  			parentDir = realOp.Dir.Ref
   267  			lastSetAttr = realOp
   268  		default:
   269  			return nil
   270  		}
   271  	}
   272  
   273  	parentOriginal, ok := chains.originals[parentDir]
   274  	if !ok {
   275  		// If the parent dir was created as part of a squash/batch,
   276  		// there might not be any update for it, and so it might not
   277  		// appear in the `originals` map.  In that case, we can use
   278  		// the original.
   279  		parentOriginal = parentDir
   280  		ok = chains.createdOriginals[parentDir]
   281  	}
   282  	if !ok {
   283  		if chains.isDeleted(parentDir) {
   284  			// If the parent's been deleted, it doesn't matter whether
   285  			// we find the type or not.
   286  			return nil
   287  		}
   288  		fbo.log.CDebugf(ctx,
   289  			"Can't find parent dir chain, unref=%s/ref=%s, for op %s (file=%s)",
   290  			lastSetAttr.Dir.Unref, lastSetAttr.Dir.Ref,
   291  			lastSetAttr, lastSetAttr.File)
   292  
   293  		// If the parent still can't be found, then barring bugs the
   294  		// whole directory was created and deleted within this update,
   295  		// so it should be treated as deleted.
   296  		chains.deletedOriginals[cc.original] = true
   297  		return nil
   298  	}
   299  
   300  	// We have to find the current parent directory block.  If the
   301  	// file has been renamed, that might be different from parentDir
   302  	// above.
   303  	if newParent, _, ok := chains.renamedParentAndName(cc.original); ok {
   304  		parentOriginal = newParent
   305  	}
   306  
   307  	parentMostRecent, err := chains.mostRecentFromOriginalOrSame(parentOriginal)
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	// If we get down here, we have an ambiguity, and need to fetch
   313  	// the block to figure out the file type.
   314  	parentPath := data.Path{
   315  		FolderBranch: fbo.folderBranch,
   316  		Path: []data.PathNode{{
   317  			BlockPointer: parentMostRecent,
   318  			Name:         data.PathPartString{},
   319  		}},
   320  	}
   321  	parentDD, cleanupFn := fbo.newDirDataWithDBM(
   322  		makeFBOLockState(), parentPath, keybase1.UserOrTeamID(""), kmd,
   323  		newDirBlockMapMemory())
   324  	defer cleanupFn()
   325  	entries, err := parentDD.GetEntries(ctx)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	// We don't have the file name handy, so search for the pointer.
   330  	found := false
   331  	for _, entry := range entries {
   332  		if entry.BlockPointer != cc.mostRecent {
   333  			continue
   334  		}
   335  		switch entry.Type {
   336  		case data.Dir:
   337  			cc.file = false
   338  		case data.File:
   339  			cc.file = true
   340  		case data.Exec:
   341  			cc.file = true
   342  		default:
   343  			return errors.Errorf("Unexpected chain type: %s", entry.Type)
   344  		}
   345  		found = true
   346  		break
   347  	}
   348  
   349  	if !found {
   350  		// If the node can't be found, then the entry has been removed
   351  		// already, and there won't be any conflicts to resolve
   352  		// anyway.  Mark it as deleted.
   353  		chains.deletedOriginals[cc.original] = true
   354  
   355  		// However, we still might be able to determine the type of
   356  		// the entry via the `rmOp` that actually deleted it.  This
   357  		// could be important if the entry ends up being recreated due
   358  		// to a conflict with another branch (e.g. HOTPOT-719).  So we
   359  		// still want to make an attempt to recover that entry type
   360  		// from the parent chain.
   361  		parentChain, ok := chains.byOriginal[parentOriginal]
   362  		if ok {
   363  			for _, op := range parentChain.ops {
   364  				rop, ok := op.(*rmOp)
   365  				if !ok {
   366  					continue
   367  				}
   368  				unrefs := rop.Unrefs()
   369  				if len(unrefs) > 0 && unrefs[0] == cc.mostRecent {
   370  					cc.file = rop.RemovedType == data.File ||
   371  						rop.RemovedType == data.Exec
   372  					break
   373  				}
   374  			}
   375  		}
   376  	}
   377  
   378  	return nil
   379  }
   380  
   381  func (cc *crChain) remove(ctx context.Context, log logger.Logger,
   382  	revision kbfsmd.Revision) bool {
   383  	anyRemoved := false
   384  	var newOps []op
   385  	for i, currOp := range cc.ops {
   386  		info := currOp.getWriterInfo()
   387  		if info.revision == revision {
   388  			log.CDebugf(ctx, "Removing op %s from chain with mostRecent=%v",
   389  				currOp, cc.mostRecent)
   390  			if !anyRemoved {
   391  				newOps = make([]op, i, len(cc.ops)-1)
   392  				// Copy everything we've iterated over so far.
   393  				copy(newOps[:i], cc.ops[:i])
   394  				anyRemoved = true
   395  			}
   396  		} else if anyRemoved {
   397  			newOps = append(newOps, currOp)
   398  		}
   399  	}
   400  	if anyRemoved {
   401  		cc.ops = newOps
   402  	}
   403  	return anyRemoved
   404  }
   405  
   406  func (cc *crChain) hasSyncOp() bool {
   407  	for _, op := range cc.ops {
   408  		if _, ok := op.(*syncOp); ok {
   409  			return true
   410  		}
   411  	}
   412  	return false
   413  }
   414  
   415  func (cc *crChain) hasSetAttrOp() bool {
   416  	for _, op := range cc.ops {
   417  		if _, ok := op.(*setAttrOp); ok {
   418  			return true
   419  		}
   420  	}
   421  	return false
   422  }
   423  
   424  func (cc *crChain) ensurePath(op op, ptr data.BlockPointer) {
   425  	if op.getFinalPath().IsValid() {
   426  		return
   427  	}
   428  
   429  	// Use the same obfuscator for both the node's name and it's
   430  	// children, because it's too complicated to try to get the real
   431  	// parent obfuscator from the chain.
   432  	op.setFinalPath(data.Path{
   433  		FolderBranch: data.FolderBranch{
   434  			Tlf:    cc.tlfID,
   435  			Branch: data.MasterBranch,
   436  		},
   437  		Path: []data.PathNode{{
   438  			BlockPointer: ptr,
   439  			Name:         data.NewPathPartString("", cc.obfuscator),
   440  		}},
   441  		ChildObfuscator: cc.obfuscator,
   442  	})
   443  }
   444  
   445  type renameInfo struct {
   446  	originalOldParent data.BlockPointer
   447  	oldName           string
   448  	originalNewParent data.BlockPointer
   449  	newName           string
   450  }
   451  
   452  func (ri renameInfo) String() string {
   453  	return fmt.Sprintf(
   454  		"renameInfo{originalOldParent: %s, oldName: %s, originalNewParent: %s, newName: %s}",
   455  		ri.originalOldParent, ri.oldName,
   456  		ri.originalNewParent, ri.newName)
   457  }
   458  
   459  // crChains contains a crChain for every KBFS node affected by the
   460  // operations over a given set of MD updates.  The chains are indexed
   461  // by both the starting (original) and ending (most recent) pointers.
   462  // It also keeps track of which chain points to the root of the folder.
   463  type crChains struct {
   464  	byOriginal   map[data.BlockPointer]*crChain
   465  	byMostRecent map[data.BlockPointer]*crChain
   466  	originalRoot data.BlockPointer
   467  
   468  	// The original blockpointers for nodes that have been
   469  	// unreferenced or initially referenced during this chain.
   470  	deletedOriginals map[data.BlockPointer]bool
   471  	createdOriginals map[data.BlockPointer]bool
   472  
   473  	// A map from original blockpointer to the full rename operation
   474  	// of the node (from the original location of the node to the
   475  	// final locations).
   476  	renamedOriginals map[data.BlockPointer]renameInfo
   477  
   478  	// Separately track pointers for unembedded block changes.
   479  	blockChangePointers map[data.BlockPointer]bool
   480  
   481  	// Pointers that should be explicitly cleaned up in the resolution.
   482  	toUnrefPointers map[data.BlockPointer]bool
   483  
   484  	// Pointers that should explicitly *not* be cleaned up in the
   485  	// resolution.
   486  	doNotUnrefPointers map[data.BlockPointer]bool
   487  
   488  	// Also keep the info for the most recent chain MD used to
   489  	// build these chains.
   490  	mostRecentChainMDInfo KeyMetadataWithRootDirEntry
   491  
   492  	// We need to be able to track ANY BlockPointer, at any point in
   493  	// the chain, back to its original.
   494  	originals map[data.BlockPointer]data.BlockPointer
   495  
   496  	// All the resolution ops from the branch, in order.
   497  	resOps []*resolutionOp
   498  
   499  	// Make per-chain obfuscators.
   500  	makeObfuscator func() data.Obfuscator
   501  }
   502  
   503  func (ccs *crChains) addOp(ptr data.BlockPointer, op op) error {
   504  	currChain, ok := ccs.byMostRecent[ptr]
   505  	if !ok {
   506  		return errors.Errorf("Could not find chain for most recent ptr %v", ptr)
   507  	}
   508  
   509  	// Make sure this op has a valid path with an obfuscator.
   510  	currChain.ensurePath(op, ptr)
   511  	currChain.ops = append(currChain.ops, op)
   512  	return nil
   513  }
   514  
   515  // addNoopChain adds a new chain with no ops to the chains struct, if
   516  // that pointer isn't involved in any chains yet.
   517  func (ccs *crChains) addNoopChain(ptr data.BlockPointer) {
   518  	if _, ok := ccs.byMostRecent[ptr]; ok {
   519  		return
   520  	}
   521  	if _, ok := ccs.byOriginal[ptr]; ok {
   522  		return
   523  	}
   524  	if _, ok := ccs.originals[ptr]; ok {
   525  		return
   526  	}
   527  	chain := &crChain{
   528  		original:   ptr,
   529  		mostRecent: ptr,
   530  		obfuscator: ccs.makeObfuscator(),
   531  		tlfID:      ccs.mostRecentChainMDInfo.TlfID(),
   532  	}
   533  	ccs.byOriginal[ptr] = chain
   534  	ccs.byMostRecent[ptr] = chain
   535  }
   536  
   537  func (ccs *crChains) makeChainForOp(op op) error {
   538  	// Ignore gc ops -- their unref semantics differ from the other
   539  	// ops.  Note that this only matters for old gcOps: new gcOps
   540  	// only unref the block ID, and not the whole pointer, so they
   541  	// wouldn't confuse chain creation.
   542  	if _, isGCOp := op.(*GCOp); isGCOp {
   543  		return nil
   544  	}
   545  
   546  	// First set the pointers for all updates, and track what's been
   547  	// created and destroyed.
   548  	for _, update := range op.allUpdates() {
   549  		chain, ok := ccs.byMostRecent[update.Unref]
   550  		if !ok {
   551  			// No matching chain means it's time to start a new chain
   552  			chain = &crChain{
   553  				original:   update.Unref,
   554  				obfuscator: ccs.makeObfuscator(),
   555  				tlfID:      ccs.mostRecentChainMDInfo.TlfID(),
   556  			}
   557  			ccs.byOriginal[update.Unref] = chain
   558  		}
   559  		if chain.mostRecent.IsInitialized() {
   560  			// delete the old most recent pointer, it's no longer needed
   561  			delete(ccs.byMostRecent, chain.mostRecent)
   562  		}
   563  		chain.mostRecent = update.Ref
   564  		ccs.byMostRecent[update.Ref] = chain
   565  		if chain.original != update.Ref {
   566  			// Always be able to track this one back to its original.
   567  			ccs.originals[update.Ref] = chain.original
   568  		}
   569  	}
   570  
   571  	for _, ptr := range op.Refs() {
   572  		ccs.createdOriginals[ptr] = true
   573  	}
   574  
   575  	for _, ptr := range op.Unrefs() {
   576  		// Look up the original pointer corresponding to this most
   577  		// recent one.
   578  		original, ok := ccs.originals[ptr]
   579  		if !ok {
   580  			original = ptr
   581  		}
   582  
   583  		// If it has already been updated to something else, we should
   584  		// just ignore this unref.  See KBFS-3258.
   585  		if chain, ok := ccs.byOriginal[original]; ok {
   586  			if ptr != chain.mostRecent {
   587  				continue
   588  			}
   589  		}
   590  
   591  		ccs.deletedOriginals[original] = true
   592  	}
   593  
   594  	// then set the op depending on the actual op type
   595  	switch realOp := op.(type) {
   596  	default:
   597  		panic(fmt.Sprintf("Unrecognized operation: %v", op))
   598  	case *createOp:
   599  		err := ccs.addOp(realOp.Dir.Ref, op)
   600  		if err != nil {
   601  			return err
   602  		}
   603  	case *rmOp:
   604  		err := ccs.addOp(realOp.Dir.Ref, op)
   605  		if err != nil {
   606  			return err
   607  		}
   608  
   609  		if len(op.Unrefs()) == 0 {
   610  			// This might be an rmOp of a file that was created and
   611  			// removed within a single batch.  If it's been renamed,
   612  			// we should also mark it as deleted to avoid confusing
   613  			// the CR code.
   614  			chain, ok := ccs.byMostRecent[realOp.Dir.Ref]
   615  			if !ok {
   616  				return errors.Errorf("No chain for rmOp dir %v", realOp.Dir.Ref)
   617  			}
   618  			for original, ri := range ccs.renamedOriginals {
   619  				if ri.originalNewParent == chain.original &&
   620  					ri.newName == realOp.OldName {
   621  					ccs.deletedOriginals[original] = true
   622  					break
   623  				}
   624  			}
   625  		}
   626  
   627  	case *renameOp:
   628  		// split rename op into two separate operations, one for
   629  		// remove and one for create
   630  		ro, err := newRmOp(
   631  			realOp.OldName, realOp.OldDir.Unref, realOp.RenamedType)
   632  		if err != nil {
   633  			return err
   634  		}
   635  		ro.setWriterInfo(realOp.getWriterInfo())
   636  		ro.setLocalTimestamp(realOp.getLocalTimestamp())
   637  		// realOp.OldDir.Ref may be zero if this is a
   638  		// post-resolution chain, so set ro.Dir.Ref manually.
   639  		ro.Dir.Ref = realOp.OldDir.Ref
   640  		err = ccs.addOp(realOp.OldDir.Ref, ro)
   641  		if err != nil {
   642  			return err
   643  		}
   644  
   645  		ndu := realOp.NewDir.Unref
   646  		ndr := realOp.NewDir.Ref
   647  		if realOp.NewDir == (blockUpdate{}) {
   648  			// this is a rename within the same directory
   649  			ndu = realOp.OldDir.Unref
   650  			ndr = realOp.OldDir.Ref
   651  		}
   652  
   653  		if len(realOp.Unrefs()) > 0 {
   654  			// Something was overwritten; make an explicit rm for it
   655  			// so we can check for conflicts.
   656  			roOverwrite, err := newRmOp(
   657  				realOp.NewName, ndu, data.File)
   658  			if err != nil {
   659  				return err
   660  			}
   661  			roOverwrite.setWriterInfo(realOp.getWriterInfo())
   662  			err = roOverwrite.Dir.setRef(ndr)
   663  			if err != nil {
   664  				return err
   665  			}
   666  			err = ccs.addOp(ndr, roOverwrite)
   667  			if err != nil {
   668  				return err
   669  			}
   670  			// Transfer any unrefs over.
   671  			for _, ptr := range realOp.Unrefs() {
   672  				roOverwrite.AddUnrefBlock(ptr)
   673  			}
   674  		}
   675  
   676  		co, err := newCreateOp(realOp.NewName, ndu, realOp.RenamedType)
   677  		if err != nil {
   678  			return err
   679  		}
   680  		co.setWriterInfo(realOp.getWriterInfo())
   681  		co.setLocalTimestamp(realOp.getLocalTimestamp())
   682  		co.renamed = true
   683  		// ndr may be zero if this is a post-resolution chain,
   684  		// so set co.Dir.Ref manually.
   685  		co.Dir.Ref = ndr
   686  		err = ccs.addOp(ndr, co)
   687  		if err != nil {
   688  			return err
   689  		}
   690  
   691  		// also keep track of the new parent for the renamed node
   692  		if realOp.Renamed.IsInitialized() {
   693  			newParentChain, ok := ccs.byMostRecent[ndr]
   694  			if !ok {
   695  				return errors.Errorf("While renaming, couldn't find the chain "+
   696  					"for the new parent %v", ndr)
   697  			}
   698  			oldParentChain, ok := ccs.byMostRecent[realOp.OldDir.Ref]
   699  			if !ok {
   700  				return errors.Errorf("While renaming, couldn't find the chain "+
   701  					"for the old parent %v", ndr)
   702  			}
   703  
   704  			renamedOriginal := realOp.Renamed
   705  			if renamedChain, ok := ccs.byMostRecent[realOp.Renamed]; ok {
   706  				renamedOriginal = renamedChain.original
   707  			}
   708  			// Use the previous old info if there is one already,
   709  			// in case this node has been renamed multiple times.
   710  			ri, ok := ccs.renamedOriginals[renamedOriginal]
   711  			if !ok {
   712  				// Otherwise make a new one.
   713  				ri = renameInfo{
   714  					originalOldParent: oldParentChain.original,
   715  					oldName:           realOp.OldName,
   716  				}
   717  			}
   718  			ri.originalNewParent = newParentChain.original
   719  			ri.newName = realOp.NewName
   720  			ccs.renamedOriginals[renamedOriginal] = ri
   721  			// Remember what you create, in case we need to merge
   722  			// directories after a rename.
   723  			co.AddRefBlock(renamedOriginal)
   724  		}
   725  	case *syncOp:
   726  		err := ccs.addOp(realOp.File.Ref, op)
   727  		if err != nil {
   728  			return err
   729  		}
   730  	case *setAttrOp:
   731  		// Because the attributes apply to the file, which doesn't
   732  		// actually have an updated pointer, we may need to create a
   733  		// new chain.
   734  		_, ok := ccs.byMostRecent[realOp.File]
   735  		if !ok {
   736  			// pointer didn't change, so most recent is the same:
   737  			chain := &crChain{
   738  				original:   realOp.File,
   739  				mostRecent: realOp.File,
   740  				obfuscator: ccs.makeObfuscator(),
   741  				tlfID:      ccs.mostRecentChainMDInfo.TlfID(),
   742  			}
   743  			ccs.byOriginal[realOp.File] = chain
   744  			ccs.byMostRecent[realOp.File] = chain
   745  		}
   746  
   747  		err := ccs.addOp(realOp.File, op)
   748  		if err != nil {
   749  			return err
   750  		}
   751  	case *resolutionOp:
   752  		ccs.resOps = append(ccs.resOps, realOp)
   753  	case *rekeyOp:
   754  		// ignore rekey op
   755  	case *GCOp:
   756  		// ignore gc op
   757  	}
   758  
   759  	return nil
   760  }
   761  
   762  func (ccs *crChains) makeChainForNewOpWithUpdate(
   763  	targetPtr data.BlockPointer, newOp op, update *blockUpdate) error {
   764  	oldUpdate := *update
   765  	// so that most recent == original
   766  	var err error
   767  	*update, err = makeBlockUpdate(targetPtr, update.Unref)
   768  	if err != nil {
   769  		return err
   770  	}
   771  	defer func() {
   772  		// reset the update to its original state before returning.
   773  		*update = oldUpdate
   774  	}()
   775  	err = ccs.makeChainForOp(newOp)
   776  	if err != nil {
   777  		return err
   778  	}
   779  	return nil
   780  }
   781  
   782  // makeChainForNewOp makes a new chain for an op that does not yet
   783  // have its pointers initialized.  It does so by setting Unref and Ref
   784  // to be the same for the duration of this function, and calling the
   785  // usual makeChainForOp method.  This function is not goroutine-safe
   786  // with respect to newOp.  Also note that rename ops will not be split
   787  // into two ops; they will be placed only in the new directory chain.
   788  func (ccs *crChains) makeChainForNewOp(targetPtr data.BlockPointer, newOp op) error {
   789  	switch realOp := newOp.(type) {
   790  	case *createOp:
   791  		return ccs.makeChainForNewOpWithUpdate(targetPtr, newOp, &realOp.Dir)
   792  	case *rmOp:
   793  		return ccs.makeChainForNewOpWithUpdate(targetPtr, newOp, &realOp.Dir)
   794  	case *renameOp:
   795  		// In this case, we don't want to split the rename chain, so
   796  		// just make up a new operation and later overwrite it with
   797  		// the rename op.
   798  		co, err := newCreateOp(realOp.NewName, realOp.NewDir.Unref, data.File)
   799  		if err != nil {
   800  			return err
   801  		}
   802  		err = ccs.makeChainForNewOpWithUpdate(targetPtr, co, &co.Dir)
   803  		if err != nil {
   804  			return err
   805  		}
   806  		chain, ok := ccs.byMostRecent[targetPtr]
   807  		if !ok {
   808  			return errors.Errorf("Couldn't find chain for %v after making it",
   809  				targetPtr)
   810  		}
   811  		if len(chain.ops) != 1 {
   812  			return errors.Errorf("Chain of unexpected length for %v after "+
   813  				"making it", targetPtr)
   814  		}
   815  		chain.ops[0] = realOp
   816  		return nil
   817  	case *setAttrOp:
   818  		return ccs.makeChainForNewOpWithUpdate(targetPtr, newOp, &realOp.Dir)
   819  	case *syncOp:
   820  		return ccs.makeChainForNewOpWithUpdate(targetPtr, newOp, &realOp.File)
   821  	default:
   822  		return errors.Errorf("Couldn't make chain with unknown operation %s",
   823  			newOp)
   824  	}
   825  }
   826  
   827  func (ccs *crChains) mostRecentFromOriginal(original data.BlockPointer) (
   828  	data.BlockPointer, error) {
   829  	chain, ok := ccs.byOriginal[original]
   830  	if !ok {
   831  		return data.BlockPointer{}, errors.WithStack(NoChainFoundError{original})
   832  	}
   833  	return chain.mostRecent, nil
   834  }
   835  
   836  func (ccs *crChains) mostRecentFromOriginalOrSame(original data.BlockPointer) (
   837  	data.BlockPointer, error) {
   838  	ptr, err := ccs.mostRecentFromOriginal(original)
   839  	if err == nil {
   840  		// A satisfactory chain was found.
   841  		return ptr, nil
   842  	} else if _, ok := errors.Cause(err).(NoChainFoundError); !ok {
   843  		// An unexpected error!
   844  		return data.BlockPointer{}, err
   845  	}
   846  	return original, nil
   847  }
   848  
   849  func (ccs *crChains) originalFromMostRecent(mostRecent data.BlockPointer) (
   850  	data.BlockPointer, error) {
   851  	chain, ok := ccs.byMostRecent[mostRecent]
   852  	if !ok {
   853  		return data.BlockPointer{}, errors.WithStack(NoChainFoundError{mostRecent})
   854  	}
   855  	return chain.original, nil
   856  }
   857  
   858  func (ccs *crChains) originalFromMostRecentOrSame(mostRecent data.BlockPointer) (
   859  	data.BlockPointer, error) {
   860  	ptr, err := ccs.originalFromMostRecent(mostRecent)
   861  	if err == nil {
   862  		// A satisfactory chain was found.
   863  		return ptr, nil
   864  	} else if _, ok := errors.Cause(err).(NoChainFoundError); !ok {
   865  		// An unexpected error!
   866  		return data.BlockPointer{}, err
   867  	}
   868  	return mostRecent, nil
   869  }
   870  
   871  func (ccs *crChains) isCreated(original data.BlockPointer) bool {
   872  	return ccs.createdOriginals[original]
   873  }
   874  
   875  func (ccs *crChains) isDeleted(original data.BlockPointer) bool {
   876  	return ccs.deletedOriginals[original]
   877  }
   878  
   879  func (ccs *crChains) renamedParentAndName(original data.BlockPointer) (
   880  	data.BlockPointer, string, bool) {
   881  	info, ok := ccs.renamedOriginals[original]
   882  	if !ok {
   883  		return data.BlockPointer{}, "", false
   884  	}
   885  	return info.originalNewParent, info.newName, true
   886  }
   887  
   888  func newCRChainsEmpty(makeObfuscator func() data.Obfuscator) *crChains {
   889  	return &crChains{
   890  		byOriginal:          make(map[data.BlockPointer]*crChain),
   891  		byMostRecent:        make(map[data.BlockPointer]*crChain),
   892  		deletedOriginals:    make(map[data.BlockPointer]bool),
   893  		createdOriginals:    make(map[data.BlockPointer]bool),
   894  		renamedOriginals:    make(map[data.BlockPointer]renameInfo),
   895  		blockChangePointers: make(map[data.BlockPointer]bool),
   896  		toUnrefPointers:     make(map[data.BlockPointer]bool),
   897  		doNotUnrefPointers:  make(map[data.BlockPointer]bool),
   898  		originals:           make(map[data.BlockPointer]data.BlockPointer),
   899  		makeObfuscator:      makeObfuscator,
   900  	}
   901  }
   902  
   903  func (ccs *crChains) addOps(codec kbfscodec.Codec,
   904  	privateMD PrivateMetadata, winfo writerInfo,
   905  	localTimestamp time.Time) error {
   906  	// Copy the ops since CR will change them.
   907  	var oldOps opsList
   908  	if privateMD.Changes.Info.BlockPointer.IsInitialized() {
   909  		// In some cases (e.g., journaling) we might not have been
   910  		// able to re-embed the block changes.  So use the cached
   911  		// version directly.
   912  		oldOps = privateMD.cachedChanges.Ops
   913  	} else {
   914  		oldOps = privateMD.Changes.Ops
   915  	}
   916  	ops := make(opsList, len(oldOps))
   917  	err := kbfscodec.Update(codec, &ops, oldOps)
   918  	if err != nil {
   919  		return err
   920  	}
   921  
   922  	for i, op := range ops {
   923  		op.setFinalPath(oldOps[i].getFinalPath())
   924  		op.setWriterInfo(winfo)
   925  		op.setLocalTimestamp(localTimestamp)
   926  		err := ccs.makeChainForOp(op)
   927  		if err != nil {
   928  			return err
   929  		}
   930  	}
   931  	return nil
   932  }
   933  
   934  // chainMetadata is the interface for metadata objects that can be
   935  // used in building crChains. It is implemented by
   936  // ImmutableRootMetadata, but is also mostly implemented by
   937  // RootMetadata (just need LastModifyingWriterVerifyingKey
   938  // LocalTimestamp).
   939  type chainMetadata interface {
   940  	KeyMetadataWithRootDirEntry
   941  	IsWriterMetadataCopiedSet() bool
   942  	LastModifyingWriter() keybase1.UID
   943  	LastModifyingWriterVerifyingKey() kbfscrypto.VerifyingKey
   944  	Revision() kbfsmd.Revision
   945  	Data() *PrivateMetadata
   946  	LocalTimestamp() time.Time
   947  }
   948  
   949  // newCRChains builds a new crChains object from the given list of
   950  // chainMetadatas, which must be non-empty.
   951  func newCRChains(
   952  	ctx context.Context, codec kbfscodec.Codec, osg idutil.OfflineStatusGetter,
   953  	chainMDs []chainMetadata, fbo *folderBlockOps, identifyTypes bool) (
   954  	ccs *crChains, err error) {
   955  	if fbo != nil {
   956  		ccs = newCRChainsEmpty(fbo.obfuscatorMaker())
   957  	} else {
   958  		ccs = newCRChainsEmpty(func() data.Obfuscator { return nil })
   959  	}
   960  
   961  	mostRecentMD := chainMDs[len(chainMDs)-1]
   962  	ccs.mostRecentChainMDInfo = mostRecentMD
   963  
   964  	// For each MD update, turn each update in each op into map
   965  	// entries and create chains for the BlockPointers that are
   966  	// affected directly by the operation.
   967  	for _, chainMD := range chainMDs {
   968  		// No new operations in these.
   969  		if chainMD.IsWriterMetadataCopiedSet() {
   970  			continue
   971  		}
   972  
   973  		offline := keybase1.OfflineAvailability_NONE
   974  		if osg != nil {
   975  			offline = osg.OfflineAvailabilityForID(chainMD.TlfID())
   976  		}
   977  
   978  		winfo := newWriterInfo(
   979  			chainMD.LastModifyingWriter(),
   980  			chainMD.LastModifyingWriterVerifyingKey(),
   981  			chainMD.Revision(), offline)
   982  		if err != nil {
   983  			return nil, err
   984  		}
   985  
   986  		chainData := *chainMD.Data()
   987  
   988  		err = ccs.addOps(codec, chainData, winfo, chainMD.LocalTimestamp())
   989  
   990  		if ptr := chainData.cachedChanges.Info.BlockPointer; ptr != data.ZeroPtr {
   991  			ccs.blockChangePointers[ptr] = true
   992  
   993  			// Any child block change pointers?
   994  			infos, err := fbo.GetIndirectFileBlockInfos(
   995  				ctx, makeFBOLockState(), chainMD,
   996  				data.Path{
   997  					FolderBranch: fbo.folderBranch,
   998  					Path: []data.PathNode{{
   999  						BlockPointer: ptr,
  1000  						Name: data.NewPathPartString(
  1001  							fmt.Sprintf("<MD rev %d>", chainMD.Revision()),
  1002  							nil),
  1003  					}},
  1004  				})
  1005  			if err != nil {
  1006  				return nil, err
  1007  			}
  1008  			for _, info := range infos {
  1009  				ccs.blockChangePointers[info.BlockPointer] = true
  1010  			}
  1011  		}
  1012  
  1013  		if err != nil {
  1014  			return nil, err
  1015  		}
  1016  
  1017  		if !ccs.originalRoot.IsInitialized() {
  1018  			// Find the original pointer for the root directory
  1019  			if rootChain, ok :=
  1020  				ccs.byMostRecent[chainData.Dir.BlockPointer]; ok {
  1021  				ccs.originalRoot = rootChain.original
  1022  			}
  1023  		}
  1024  	}
  1025  
  1026  	for _, chain := range ccs.byOriginal {
  1027  		toUnrefs := chain.collapse(ccs.createdOriginals, ccs.originals)
  1028  		for _, unref := range toUnrefs {
  1029  			ccs.toUnrefPointers[unref] = true
  1030  		}
  1031  		// NOTE: even if we've removed all its ops, still keep the
  1032  		// chain around so we can see the mapping between the original
  1033  		// and most recent pointers.
  1034  
  1035  		// Figure out if this chain is a file or directory.  We don't
  1036  		// need to do this for chains that represent a resolution in
  1037  		// progress, since in that case all actions are already
  1038  		// completed.
  1039  		if identifyTypes {
  1040  			err := chain.identifyType(ctx, fbo, mostRecentMD, ccs)
  1041  			if err != nil {
  1042  				return nil, err
  1043  			}
  1044  		}
  1045  	}
  1046  
  1047  	return ccs, nil
  1048  }
  1049  
  1050  // newCRChainsForIRMDs simply builds a list of chainMetadatas from the
  1051  // given list of ImmutableRootMetadatas and calls newCRChains with it.
  1052  func newCRChainsForIRMDs(
  1053  	ctx context.Context, codec kbfscodec.Codec, osg idutil.OfflineStatusGetter,
  1054  	irmds []ImmutableRootMetadata, fbo *folderBlockOps,
  1055  	identifyTypes bool) (ccs *crChains, err error) {
  1056  	chainMDs := make([]chainMetadata, len(irmds))
  1057  	for i, irmd := range irmds {
  1058  		chainMDs[i] = irmd
  1059  	}
  1060  	return newCRChains(ctx, codec, osg, chainMDs, fbo, identifyTypes)
  1061  }
  1062  
  1063  type crChainSummary struct {
  1064  	Path string
  1065  	Ops  []string
  1066  }
  1067  
  1068  func (ccs *crChains) summary(identifyChains *crChains,
  1069  	nodeCache NodeCache) (res []*crChainSummary) {
  1070  	for _, chain := range ccs.byOriginal {
  1071  		summary := &crChainSummary{}
  1072  		res = append(res, summary)
  1073  
  1074  		// first stringify all the ops so they are displayed even if
  1075  		// we can't find the path.
  1076  		for _, op := range chain.ops {
  1077  			summary.Ops = append(summary.Ops, op.String())
  1078  		}
  1079  
  1080  		// find the path name using the identified most recent pointer
  1081  		n := nodeCache.Get(chain.mostRecent.Ref())
  1082  		if n == nil {
  1083  			summary.Path = fmt.Sprintf("Unknown path: %v", chain.mostRecent)
  1084  			continue
  1085  		}
  1086  
  1087  		path := nodeCache.PathFromNode(n)
  1088  		summary.Path = path.String()
  1089  	}
  1090  
  1091  	return res
  1092  }
  1093  
  1094  func (ccs *crChains) removeChain(ptr data.BlockPointer) {
  1095  	if chain, ok := ccs.byMostRecent[ptr]; ok {
  1096  		delete(ccs.byOriginal, chain.original)
  1097  	} else {
  1098  		delete(ccs.byOriginal, ptr)
  1099  	}
  1100  	delete(ccs.byMostRecent, ptr)
  1101  }
  1102  
  1103  // copyOpAndRevertUnrefsToOriginals returns a shallow copy of the op,
  1104  // modifying each custom BlockPointer field to reference the original
  1105  // version of the corresponding blocks.
  1106  func (ccs *crChains) copyOpAndRevertUnrefsToOriginals(currOp op) op {
  1107  	var unrefs []*data.BlockPointer
  1108  	var newOp op
  1109  	switch realOp := currOp.(type) {
  1110  	case *createOp:
  1111  		newCreateOp := *realOp
  1112  		unrefs = append(unrefs, &newCreateOp.Dir.Unref)
  1113  		newOp = &newCreateOp
  1114  	case *rmOp:
  1115  		newRmOp := *realOp
  1116  		unrefs = append(unrefs, &newRmOp.Dir.Unref)
  1117  		newOp = &newRmOp
  1118  	case *renameOp:
  1119  		newRenameOp := *realOp
  1120  		unrefs = append(unrefs, &newRenameOp.OldDir.Unref,
  1121  			&newRenameOp.NewDir.Unref, &newRenameOp.Renamed)
  1122  		newOp = &newRenameOp
  1123  	case *syncOp:
  1124  		newSyncOp := *realOp
  1125  		unrefs = append(unrefs, &newSyncOp.File.Unref)
  1126  		newOp = &newSyncOp
  1127  	case *setAttrOp:
  1128  		newSetAttrOp := *realOp
  1129  		unrefs = append(unrefs, &newSetAttrOp.Dir.Unref, &newSetAttrOp.File)
  1130  		newOp = &newSetAttrOp
  1131  	case *GCOp:
  1132  		// No need to copy a GCOp, it won't be modified
  1133  		newOp = realOp
  1134  	case *rekeyOp:
  1135  		newOp = realOp
  1136  	}
  1137  	for _, unref := range unrefs {
  1138  		ok := true
  1139  		// Loop over the originals, since if `changeOriginal` was
  1140  		// called, there might be a path of them.
  1141  		for ok {
  1142  			var original data.BlockPointer
  1143  			original, ok = ccs.originals[*unref]
  1144  			if ok {
  1145  				*unref = original
  1146  			}
  1147  		}
  1148  	}
  1149  	return newOp
  1150  }
  1151  
  1152  // changeOriginal converts the original of a chain to a different
  1153  // original, which originated in some other branch.
  1154  func (ccs *crChains) changeOriginal(oldOriginal data.BlockPointer,
  1155  	newOriginal data.BlockPointer) error {
  1156  	if oldOriginal == newOriginal {
  1157  		// This apparently can happen, but I'm not sure how.  (See
  1158  		// KBFS-2946.)  Maybe because of a self-conflict in some weird
  1159  		// error conditions. In this case, we can just pretend the
  1160  		// change worked, and let CR continue it's normal process.
  1161  		return nil
  1162  	}
  1163  	chain, ok := ccs.byOriginal[oldOriginal]
  1164  	if !ok {
  1165  		return errors.WithStack(NoChainFoundError{oldOriginal})
  1166  	}
  1167  	if _, ok := ccs.byOriginal[newOriginal]; ok {
  1168  		return errors.Errorf("crChains.changeOriginal: New original %v "+
  1169  			"already exists", newOriginal)
  1170  	}
  1171  
  1172  	delete(ccs.byOriginal, oldOriginal)
  1173  	chain.original = newOriginal
  1174  	ccs.byOriginal[newOriginal] = chain
  1175  	ccs.originals[oldOriginal] = newOriginal
  1176  
  1177  	if _, ok := ccs.deletedOriginals[oldOriginal]; ok {
  1178  		delete(ccs.deletedOriginals, oldOriginal)
  1179  		ccs.deletedOriginals[newOriginal] = true
  1180  	}
  1181  	delete(ccs.createdOriginals, oldOriginal)
  1182  	// We're swapping in an original made on some other branch, so
  1183  	// it shouldn't go in the `createdOriginals` map.
  1184  	if ri, ok := ccs.renamedOriginals[oldOriginal]; ok {
  1185  		delete(ccs.renamedOriginals, oldOriginal)
  1186  		ccs.renamedOriginals[newOriginal] = ri
  1187  	}
  1188  	for p, info := range ccs.renamedOriginals {
  1189  		changed := false
  1190  		if info.originalOldParent == oldOriginal {
  1191  			info.originalOldParent = newOriginal
  1192  			changed = true
  1193  		}
  1194  		if info.originalNewParent == oldOriginal {
  1195  			info.originalNewParent = newOriginal
  1196  			changed = true
  1197  		}
  1198  		if changed {
  1199  			ccs.renamedOriginals[p] = info
  1200  		}
  1201  	}
  1202  	return nil
  1203  }
  1204  
  1205  func (ccs *crChains) findPathForDeleted(mostRecent data.BlockPointer) data.Path {
  1206  	// Find the parent chain that deleted this one.
  1207  	for ptr, chain := range ccs.byMostRecent {
  1208  		for _, op := range chain.ops {
  1209  			ro, ok := op.(*rmOp)
  1210  			if !ok {
  1211  				continue
  1212  			}
  1213  			for _, unref := range ro.Unrefs() {
  1214  				if unref == mostRecent {
  1215  					// If the path isn't set yet, recurse.
  1216  					p := ro.getFinalPath()
  1217  					if !p.IsValid() {
  1218  						p = ccs.findPathForDeleted(ptr)
  1219  						ro.setFinalPath(p)
  1220  					}
  1221  					return p.ChildPath(
  1222  						ro.obfuscatedOldName(), mostRecent,
  1223  						ccs.makeObfuscator())
  1224  				}
  1225  			}
  1226  		}
  1227  	}
  1228  	// We can get here if the entry in question was also created
  1229  	// during the chain period, in which case `mostRecent` doesn't
  1230  	// need to be unreferenced explicitly.  There's nothing easy we
  1231  	// can do here.  But the path isn't very important; for the most
  1232  	// part, it's just informational for log messages and journal
  1233  	// status.  So if we are stuck, just pick use the root directory
  1234  	// and a fake name.
  1235  	var rootMostRecent data.BlockPointer
  1236  	if ccs.mostRecentChainMDInfo != nil {
  1237  		rootMostRecent =
  1238  			ccs.mostRecentChainMDInfo.GetRootDirEntry().BlockPointer
  1239  	}
  1240  	return data.Path{
  1241  		FolderBranch: data.FolderBranch{
  1242  			Tlf:    ccs.mostRecentChainMDInfo.TlfID(),
  1243  			Branch: data.MasterBranch,
  1244  		},
  1245  		Path: []data.PathNode{{
  1246  			BlockPointer: rootMostRecent,
  1247  			Name: data.NewPathPartString(
  1248  				mostRecent.String(), ccs.makeObfuscator()),
  1249  		}},
  1250  	}
  1251  }
  1252  
  1253  func (ccs *crChains) findPathForCreated(createdChain *crChain) data.Path {
  1254  	mostRecent := createdChain.mostRecent
  1255  
  1256  	// Find the parent chain that deleted this one.
  1257  	for _, chain := range ccs.byMostRecent {
  1258  		for _, op := range chain.ops {
  1259  			co, ok := op.(*createOp)
  1260  			if !ok {
  1261  				continue
  1262  			}
  1263  			for _, ref := range co.Refs() {
  1264  				if ref == createdChain.original {
  1265  					// If the path isn't set yet, recurse.
  1266  					p := co.getFinalPath()
  1267  					if !p.IsValid() {
  1268  						p = ccs.findPathForCreated(chain)
  1269  						co.setFinalPath(p)
  1270  					}
  1271  					return p.ChildPath(
  1272  						co.obfuscatedNewName(), mostRecent,
  1273  						ccs.makeObfuscator())
  1274  				}
  1275  			}
  1276  		}
  1277  	}
  1278  	// We can get here if the entry in question was also deleted
  1279  	// during the chain period, in which case `mostRecent` doesn't
  1280  	// need to be unreferenced explicitly.  There's nothing easy we
  1281  	// can do here.  But the path isn't very important; for the most
  1282  	// part, it's just informational for log messages and journal
  1283  	// status.  So if we are stuck, just pick use the root directory
  1284  	// and a fake name.
  1285  	var rootMostRecent data.BlockPointer
  1286  	if ccs.mostRecentChainMDInfo != nil {
  1287  		rootMostRecent =
  1288  			ccs.mostRecentChainMDInfo.GetRootDirEntry().BlockPointer
  1289  	}
  1290  	return data.Path{
  1291  		FolderBranch: data.FolderBranch{
  1292  			Tlf:    ccs.mostRecentChainMDInfo.TlfID(),
  1293  			Branch: data.MasterBranch,
  1294  		},
  1295  		Path: []data.PathNode{{
  1296  			BlockPointer: rootMostRecent,
  1297  			Name: data.NewPathPartString(
  1298  				mostRecent.String(), ccs.makeObfuscator()),
  1299  		}},
  1300  	}
  1301  }
  1302  
  1303  // getPaths returns a sorted slice of most recent paths to all the
  1304  // nodes in the given CR chains that were directly modified during a
  1305  // branch, and which existed at both the start and the end of the
  1306  // branch.  This represents the paths that need to be checked for
  1307  // conflicts.  The paths are sorted by descending path length.  It
  1308  // uses nodeCache when looking up paths, which must at least contain
  1309  // the most recent root node of the branch.  Note that if a path
  1310  // cannot be found, the corresponding chain is completely removed from
  1311  // the set of CR chains.  Set includeCreates to true if the returned
  1312  // paths should include the paths of newly-created nodes.
  1313  func (ccs *crChains) getPaths(ctx context.Context, blocks *folderBlockOps,
  1314  	log logger.Logger, nodeCache NodeCache, includeCreates bool,
  1315  	checkOpFinalPaths bool) ([]data.Path, error) {
  1316  	newPtrs := make(map[data.BlockPointer]bool)
  1317  	var ptrs []data.BlockPointer
  1318  	renameOps := make(map[data.BlockPointer][]*renameOp)
  1319  	for ptr, chain := range ccs.byMostRecent {
  1320  		newPtrs[ptr] = true
  1321  		// We only care about the paths for ptrs that are directly
  1322  		// affected by operations and were live through the entire
  1323  		// unmerged branch, or, if includeCreates was set, was created
  1324  		// and not deleted in the unmerged branch.
  1325  		if len(chain.ops) > 0 &&
  1326  			(includeCreates || !ccs.isCreated(chain.original)) &&
  1327  			!ccs.isDeleted(chain.original) {
  1328  			ptrs = append(ptrs, ptr)
  1329  			// Also resolve the old name for any renames, if needed.
  1330  			for _, op := range chain.ops {
  1331  				ro, ok := op.(*renameOp)
  1332  				if !ok {
  1333  					continue
  1334  				}
  1335  
  1336  				oldDirPtr := ro.OldDir.Ref
  1337  				if ro.NewDir != (blockUpdate{}) {
  1338  					if !newPtrs[oldDirPtr] {
  1339  						ptrs = append(ptrs, oldDirPtr)
  1340  						newPtrs[oldDirPtr] = true
  1341  					}
  1342  					renameOps[oldDirPtr] = append(renameOps[oldDirPtr], ro)
  1343  				}
  1344  			}
  1345  		}
  1346  	}
  1347  
  1348  	if checkOpFinalPaths {
  1349  		// If we plan to check all the paths, clear them out first.
  1350  		for _, chain := range ccs.byMostRecent {
  1351  			for _, op := range chain.ops {
  1352  				op.setFinalPath(data.Path{})
  1353  			}
  1354  		}
  1355  	}
  1356  
  1357  	pathMap, err := blocks.SearchForPaths(ctx, nodeCache, ptrs,
  1358  		newPtrs, ccs.mostRecentChainMDInfo,
  1359  		ccs.mostRecentChainMDInfo.GetRootDirEntry().BlockPointer)
  1360  	if err != nil {
  1361  		return nil, err
  1362  	}
  1363  
  1364  	paths := make([]data.Path, 0, len(pathMap))
  1365  	for ptr, p := range pathMap {
  1366  		if len(p.Path) == 0 {
  1367  			log.CDebugf(ctx, "Ignoring pointer with no found path: %v", ptr)
  1368  			ccs.removeChain(ptr)
  1369  			continue
  1370  		}
  1371  		paths = append(paths, p)
  1372  
  1373  		// update the unmerged final paths
  1374  		if chain, ok := ccs.byMostRecent[ptr]; ok {
  1375  			for _, op := range chain.ops {
  1376  				op.setFinalPath(p)
  1377  			}
  1378  		}
  1379  		for _, ro := range renameOps[ptr] {
  1380  			ro.oldFinalPath = p
  1381  		}
  1382  	}
  1383  
  1384  	// Fill in the paths for any deleted entries.
  1385  	for original := range ccs.deletedOriginals {
  1386  		chain, ok := ccs.byOriginal[original]
  1387  		if !ok {
  1388  			continue
  1389  		}
  1390  		p := ccs.findPathForDeleted(chain.mostRecent)
  1391  		for _, op := range chain.ops {
  1392  			op.setFinalPath(p)
  1393  		}
  1394  	}
  1395  
  1396  	// Even if `includeCreates` is false, we still might need to have
  1397  	// the final paths set on all the ops, later during conflict
  1398  	// resolution.  For example, in the case where the same directory
  1399  	// is created in both the unmerged and merged branches, and the
  1400  	// child entries need to be compared for conflicts
  1401  	if !includeCreates {
  1402  		for original := range ccs.createdOriginals {
  1403  			chain, ok := ccs.byOriginal[original]
  1404  			if !ok {
  1405  				continue
  1406  			}
  1407  			p := ccs.findPathForCreated(chain)
  1408  			for _, op := range chain.ops {
  1409  				op.setFinalPath(p)
  1410  			}
  1411  		}
  1412  	}
  1413  
  1414  	if checkOpFinalPaths {
  1415  		for _, chain := range ccs.byMostRecent {
  1416  			for _, op := range chain.ops {
  1417  				if !op.getFinalPath().IsValid() {
  1418  					return nil, errors.Errorf(
  1419  						"Op %s doesn't have final path", op)
  1420  				}
  1421  			}
  1422  		}
  1423  	}
  1424  
  1425  	// Order by descending path length.
  1426  	sort.Sort(crSortedPaths(paths))
  1427  	return paths, nil
  1428  }
  1429  
  1430  // remove deletes all operations associated with the given revision
  1431  // from the chains.  It leaves original block pointers in place
  1432  // though, even when removing operations from the head of the chain.
  1433  // It returns the set of chains with at least one operation removed.
  1434  func (ccs *crChains) remove(ctx context.Context, log logger.Logger,
  1435  	revision kbfsmd.Revision) []*crChain {
  1436  	var chainsWithRemovals []*crChain
  1437  	for _, chain := range ccs.byOriginal {
  1438  		if chain.remove(ctx, log, revision) {
  1439  			chainsWithRemovals = append(chainsWithRemovals, chain)
  1440  		}
  1441  	}
  1442  	return chainsWithRemovals
  1443  }
  1444  
  1445  func (ccs *crChains) revertRenames(oldOps []op) error {
  1446  	for _, oldOp := range oldOps {
  1447  		if rop, ok := oldOp.(*renameOp); ok {
  1448  			// Replace the corresponding createOp, and remove the
  1449  			// rmOp.
  1450  			oldChain, ok := ccs.byMostRecent[rop.OldDir.Ref]
  1451  			if !ok {
  1452  				continue
  1453  			}
  1454  			found := false
  1455  			for i, oldOp := range oldChain.ops {
  1456  				if rmop, ok := oldOp.(*rmOp); ok &&
  1457  					rmop.OldName == rop.OldName {
  1458  					rop.oldFinalPath = rmop.getFinalPath()
  1459  					found = true
  1460  					oldChain.ops = append(
  1461  						oldChain.ops[:i], oldChain.ops[i+1:]...)
  1462  					// The first rm should be the one that matches, as
  1463  					// earlier ones should have been collapsed away
  1464  					// from the chain.
  1465  					break
  1466  				}
  1467  			}
  1468  
  1469  			if !found || !rop.oldFinalPath.IsValid() {
  1470  				// We don't need to revert any renames without an
  1471  				// rmOp, because it was probably just created and
  1472  				// renamed within a single journal update.
  1473  				continue
  1474  			}
  1475  
  1476  			newChain := oldChain
  1477  			if rop.NewDir != (blockUpdate{}) {
  1478  				newChain, ok = ccs.byMostRecent[rop.NewDir.Ref]
  1479  				if !ok {
  1480  					// There was a corresponding rmOp, and the node
  1481  					// was renamed across directories, but for some
  1482  					// unknown reason we can't find the chain for the
  1483  					// new directory.
  1484  					return errors.Errorf(
  1485  						"Cannot find new directory %s for rename op: %s",
  1486  						rop.NewDir.Ref, rop)
  1487  				}
  1488  			}
  1489  
  1490  			added := false
  1491  			for i, newOp := range newChain.ops {
  1492  				if cop, ok := newOp.(*createOp); ok &&
  1493  					cop.renamed && cop.NewName == rop.NewName {
  1494  					ropCopy := rop.deepCopy()
  1495  					ropCopy.setFinalPath(cop.getFinalPath())
  1496  					newChain.ops[i] = ropCopy
  1497  					added = true
  1498  					break
  1499  				}
  1500  			}
  1501  			if !added {
  1502  				// If we didn't find the create op to replace, then
  1503  				// this node may have been renamed and then removed,
  1504  				// with the create op being eliminated in the process.
  1505  				// We need to keep the rename op there though if there
  1506  				// is a remove operation, so that any remove
  1507  				// operations within the renamed directory are
  1508  				// processed correctly (see HOTPOT-616).
  1509  				for _, newOp := range newChain.ops {
  1510  					if rmop, ok := newOp.(*rmOp); ok &&
  1511  						rop.NewName == rmop.OldName {
  1512  						ropCopy := rop.deepCopy()
  1513  						ropCopy.setFinalPath(rmop.getFinalPath())
  1514  						newChain.ops = append([]op{ropCopy}, newChain.ops...)
  1515  						break
  1516  					}
  1517  				}
  1518  			}
  1519  		}
  1520  	}
  1521  	return nil
  1522  }