github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/ops.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"reflect"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/keybase/client/go/kbfs/data"
    15  	"github.com/keybase/client/go/kbfs/kbfscodec"
    16  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    17  	"github.com/keybase/client/go/kbfs/kbfsedits"
    18  	"github.com/keybase/client/go/kbfs/kbfsmd"
    19  	"github.com/keybase/client/go/kbfs/tlf"
    20  	"github.com/keybase/client/go/protocol/keybase1"
    21  	"github.com/keybase/go-codec/codec"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  // op represents a single file-system remote-sync operation. Note that
    26  // ops store and marshal any filenames in plaintext fields, so the
    27  // accessed fields must be handled carefully.  `String()` prints
    28  // obfuscated filenames, however.
    29  type op interface {
    30  	AddRefBlock(ptr data.BlockPointer)
    31  	DelRefBlock(ptr data.BlockPointer)
    32  	AddUnrefBlock(ptr data.BlockPointer)
    33  	DelUnrefBlock(ptr data.BlockPointer)
    34  	AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer)
    35  	SizeExceptUpdates() uint64
    36  	allUpdates() []blockUpdate
    37  	Refs() []data.BlockPointer
    38  	Unrefs() []data.BlockPointer
    39  	String() string
    40  	Plaintext() string
    41  	StringWithRefs(indent string) string
    42  	setWriterInfo(writerInfo)
    43  	getWriterInfo() writerInfo
    44  	setFinalPath(p data.Path)
    45  	getFinalPath() data.Path
    46  	setLocalTimestamp(t time.Time)
    47  	getLocalTimestamp() time.Time
    48  	checkValid() error
    49  	deepCopy() op
    50  	// checkConflict compares the function's target op with the given
    51  	// op, and returns a resolution if one is needed (or nil
    52  	// otherwise).  The resulting action (if any) assumes that this
    53  	// method's target op is the unmerged op, and the given op is the
    54  	// merged op.
    55  	checkConflict(ctx context.Context,
    56  		renamer ConflictRenamer, mergedOp op, isFile bool) (
    57  		crAction, error)
    58  	// getDefaultAction should be called on an unmerged op only after
    59  	// all conflicts with the corresponding change have been checked,
    60  	// and it returns the action to take against the merged branch
    61  	// given that there are no conflicts.
    62  	getDefaultAction(mergedPath data.Path) crAction
    63  
    64  	// AddSelfUpdate adds an update from the given pointer to itself.
    65  	// This should be used when the caller doesn't yet know what the
    66  	// new block ID will be, but wants to "complete" the update as a
    67  	// signal to a future prepping process that the block needs to be
    68  	// processed/readied, at which point the real new pointer will be
    69  	// filled in.
    70  	AddSelfUpdate(ptr data.BlockPointer)
    71  
    72  	// ToEditNotification returns an edit notification if this op
    73  	// needs one, otherwise it returns nil.
    74  	ToEditNotification(
    75  		rev kbfsmd.Revision, revTime time.Time, device kbfscrypto.VerifyingKey,
    76  		uid keybase1.UID, tlfID tlf.ID) *kbfsedits.NotificationMessage
    77  }
    78  
    79  // op codes
    80  const (
    81  	createOpCode kbfscodec.ExtCode = iota + kbfscodec.ExtCodeOpsRangeStart
    82  	rmOpCode
    83  	renameOpCode
    84  	syncOpCode
    85  	setAttrOpCode
    86  	resolutionOpCode
    87  	rekeyOpCode
    88  	gcOpCode // for deleting old blocks during an MD history truncation
    89  )
    90  
    91  // blockUpdate represents a block that was updated to have a new
    92  // BlockPointer.
    93  //
    94  // NOTE: Don't add or modify anything in this struct without
    95  // considering how old clients will handle them.
    96  type blockUpdate struct {
    97  	// TODO: Ideally, we'd omit Unref or Ref if they're
    98  	// empty. However, we'd first have to verify that there's
    99  	// nothing that relies on either one of these fields to always
   100  	// be filled (e.g., see similar comments for the Info field on
   101  	// BlockChanges.)
   102  	Unref data.BlockPointer `codec:"u"`
   103  	Ref   data.BlockPointer `codec:"r"`
   104  }
   105  
   106  func makeBlockUpdate(unref, ref data.BlockPointer) (blockUpdate, error) {
   107  	bu := blockUpdate{}
   108  	err := bu.setUnref(unref)
   109  	if err != nil {
   110  		return blockUpdate{}, err
   111  	}
   112  	err = bu.setRef(ref)
   113  	if err != nil {
   114  		return blockUpdate{}, err
   115  	}
   116  	return bu, nil
   117  }
   118  
   119  func (u blockUpdate) checkValid() error {
   120  	if u.Unref == (data.BlockPointer{}) {
   121  		return errors.New("nil unref")
   122  	}
   123  	if u.Ref == (data.BlockPointer{}) {
   124  		return errors.New("nil ref")
   125  	}
   126  	return nil
   127  }
   128  
   129  func (u *blockUpdate) setUnref(ptr data.BlockPointer) error {
   130  	if ptr == (data.BlockPointer{}) {
   131  		return errors.Errorf("setUnref called with nil ptr")
   132  	}
   133  	u.Unref = ptr
   134  	return nil
   135  }
   136  
   137  func (u *blockUpdate) setRef(ptr data.BlockPointer) error {
   138  	if ptr == (data.BlockPointer{}) {
   139  		return errors.Errorf("setRef called with nil ptr")
   140  	}
   141  	u.Ref = ptr
   142  	return nil
   143  }
   144  
   145  // list codes
   146  const (
   147  	opsListCode kbfscodec.ExtCode = iota + kbfscodec.ExtCodeListRangeStart
   148  )
   149  
   150  type opsList []op
   151  
   152  // OpCommon are data structures needed by all ops.  It is only
   153  // exported for serialization purposes.
   154  type OpCommon struct {
   155  	RefBlocks   []data.BlockPointer `codec:"r,omitempty"`
   156  	UnrefBlocks []data.BlockPointer `codec:"u,omitempty"`
   157  	Updates     []blockUpdate       `codec:"o,omitempty"`
   158  
   159  	codec.UnknownFieldSetHandler
   160  
   161  	// writerInfo is the keybase username and device that generated this
   162  	// operation.
   163  	// Not exported; only used during conflict resolution.
   164  	writerInfo writerInfo
   165  	// finalPath is the final resolved path to the node that this
   166  	// operation affects in a set of MD updates.  Not exported; only
   167  	// used locally.
   168  	finalPath data.Path
   169  	// localTimestamp should be set to the localTimestamp of the
   170  	// corresponding ImmutableRootMetadata when ops need individual
   171  	// timestamps.  Not exported; only used locally.
   172  	localTimestamp time.Time
   173  }
   174  
   175  func (oc OpCommon) deepCopy() OpCommon {
   176  	ocCopy := OpCommon{}
   177  
   178  	ocCopy.RefBlocks = make([]data.BlockPointer, len(oc.RefBlocks))
   179  	copy(ocCopy.RefBlocks, oc.RefBlocks)
   180  	ocCopy.UnrefBlocks = make([]data.BlockPointer, len(oc.UnrefBlocks))
   181  	copy(ocCopy.UnrefBlocks, oc.UnrefBlocks)
   182  	ocCopy.Updates = make([]blockUpdate, len(oc.Updates))
   183  	copy(ocCopy.Updates, oc.Updates)
   184  
   185  	// TODO: if we ever need to copy the unknown fields in this
   186  	// method, we'll have to change the codec interface to make it
   187  	// possible.
   188  
   189  	ocCopy.writerInfo = oc.writerInfo
   190  	ocCopy.finalPath = oc.finalPath
   191  	ocCopy.finalPath.Path = make([]data.PathNode, len(oc.finalPath.Path))
   192  	copy(ocCopy.finalPath.Path, oc.finalPath.Path)
   193  	ocCopy.localTimestamp = oc.localTimestamp
   194  	return ocCopy
   195  }
   196  
   197  // AddRefBlock adds this block to the list of newly-referenced blocks
   198  // for this op.
   199  func (oc *OpCommon) AddRefBlock(ptr data.BlockPointer) {
   200  	oc.RefBlocks = append(oc.RefBlocks, ptr)
   201  }
   202  
   203  // DelRefBlock removes the first reference of the given block from the
   204  // list of newly-referenced blocks for this op.
   205  func (oc *OpCommon) DelRefBlock(ptr data.BlockPointer) {
   206  	for i, ref := range oc.RefBlocks {
   207  		if ptr == ref {
   208  			oc.RefBlocks = append(oc.RefBlocks[:i], oc.RefBlocks[i+1:]...)
   209  			break
   210  		}
   211  	}
   212  }
   213  
   214  // AddUnrefBlock adds this block to the list of newly-unreferenced blocks
   215  // for this op.
   216  func (oc *OpCommon) AddUnrefBlock(ptr data.BlockPointer) {
   217  	oc.UnrefBlocks = append(oc.UnrefBlocks, ptr)
   218  }
   219  
   220  // DelUnrefBlock removes the first unreference of the given block from
   221  // the list of unreferenced blocks for this op.
   222  func (oc *OpCommon) DelUnrefBlock(ptr data.BlockPointer) {
   223  	for i, unref := range oc.UnrefBlocks {
   224  		if ptr == unref {
   225  			oc.UnrefBlocks = append(oc.UnrefBlocks[:i], oc.UnrefBlocks[i+1:]...)
   226  			break
   227  		}
   228  	}
   229  }
   230  
   231  // AddUpdate adds a mapping from an old block to the new version of
   232  // that block, for this op.
   233  func (oc *OpCommon) AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer) {
   234  	// Either pointer may be zero, if we're building an op that
   235  	// will be fixed up later.
   236  	bu := blockUpdate{oldPtr, newPtr}
   237  	oc.Updates = append(oc.Updates, bu)
   238  }
   239  
   240  // AddSelfUpdate implements the op interface for OpCommon -- see the
   241  // comment in op.
   242  func (oc *OpCommon) AddSelfUpdate(ptr data.BlockPointer) {
   243  	oc.AddUpdate(ptr, ptr)
   244  }
   245  
   246  // Refs returns a slice containing all the blocks that were initially
   247  // referenced during this op.
   248  func (oc *OpCommon) Refs() []data.BlockPointer {
   249  	return oc.RefBlocks
   250  }
   251  
   252  // Unrefs returns a slice containing all the blocks that were
   253  // unreferenced during this op.
   254  func (oc *OpCommon) Unrefs() []data.BlockPointer {
   255  	return oc.UnrefBlocks
   256  }
   257  
   258  func (oc *OpCommon) setWriterInfo(info writerInfo) {
   259  	oc.writerInfo = info
   260  }
   261  
   262  func (oc *OpCommon) getWriterInfo() writerInfo {
   263  	return oc.writerInfo
   264  }
   265  
   266  func (oc *OpCommon) setFinalPath(p data.Path) {
   267  	oc.finalPath = p
   268  }
   269  
   270  func (oc *OpCommon) getFinalPath() data.Path {
   271  	return oc.finalPath
   272  }
   273  
   274  func (oc *OpCommon) obfuscatedName(name string) data.PathPartString {
   275  	return data.NewPathPartString(name, oc.finalPath.Obfuscator())
   276  }
   277  
   278  func (oc *OpCommon) setLocalTimestamp(t time.Time) {
   279  	oc.localTimestamp = t
   280  }
   281  
   282  func (oc *OpCommon) getLocalTimestamp() time.Time {
   283  	return oc.localTimestamp
   284  }
   285  
   286  func (oc *OpCommon) checkUpdatesValid() error {
   287  	for i, update := range oc.Updates {
   288  		err := update.checkValid()
   289  		if err != nil {
   290  			return errors.Errorf(
   291  				"update[%d]=%v got error: %v", i, update, err)
   292  		}
   293  	}
   294  	return nil
   295  }
   296  
   297  func (oc *OpCommon) stringWithRefs(indent string) string {
   298  	res := ""
   299  	for i, update := range oc.Updates {
   300  		res += indent + fmt.Sprintf(
   301  			"Update[%d]: %v -> %v\n", i, update.Unref, update.Ref)
   302  	}
   303  	for i, ref := range oc.RefBlocks {
   304  		res += indent + fmt.Sprintf("Ref[%d]: %v\n", i, ref)
   305  	}
   306  	for i, unref := range oc.UnrefBlocks {
   307  		res += indent + fmt.Sprintf("Unref[%d]: %v\n", i, unref)
   308  	}
   309  	return res
   310  }
   311  
   312  // ToEditNotification implements the op interface for OpCommon.
   313  func (oc *OpCommon) ToEditNotification(
   314  	_ kbfsmd.Revision, _ time.Time, _ kbfscrypto.VerifyingKey,
   315  	_ keybase1.UID, _ tlf.ID) *kbfsedits.NotificationMessage {
   316  	// Ops embedding this that can be converted should override this.
   317  	return nil
   318  }
   319  
   320  // createOp is an op representing a file or subdirectory creation
   321  type createOp struct {
   322  	OpCommon
   323  	NewName string         `codec:"n"`
   324  	Dir     blockUpdate    `codec:"d"`
   325  	Type    data.EntryType `codec:"t"`
   326  
   327  	// If true, this create op represents half of a rename operation.
   328  	// This op should never be persisted.
   329  	renamed bool
   330  
   331  	// If true, during conflict resolution the blocks of the file will
   332  	// be copied.
   333  	forceCopy bool
   334  
   335  	// If this is set, ths create op needs to be turned has been
   336  	// turned into a symlink creation locally to avoid a cycle during
   337  	// conflict resolution, and the following field represents the
   338  	// text of the symlink. This op should never be persisted.
   339  	crSymPath string
   340  }
   341  
   342  func newCreateOp(name string, oldDir data.BlockPointer, t data.EntryType) (*createOp, error) {
   343  	co := &createOp{
   344  		NewName: name,
   345  	}
   346  	err := co.Dir.setUnref(oldDir)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  	co.Type = t
   351  	return co, nil
   352  }
   353  
   354  func (co *createOp) deepCopy() op {
   355  	coCopy := *co
   356  	coCopy.OpCommon = co.OpCommon.deepCopy()
   357  	return &coCopy
   358  }
   359  
   360  func newCreateOpForRootDir() *createOp {
   361  	return &createOp{
   362  		Type: data.Dir,
   363  	}
   364  }
   365  
   366  func (co *createOp) AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer) {
   367  	if co.Dir == (blockUpdate{}) {
   368  		panic("AddUpdate called on create op with empty Dir " +
   369  			"(probably create op for root dir)")
   370  	}
   371  	if oldPtr == co.Dir.Unref {
   372  		err := co.Dir.setRef(newPtr)
   373  		if err != nil {
   374  			panic(err)
   375  		}
   376  		return
   377  	}
   378  	co.OpCommon.AddUpdate(oldPtr, newPtr)
   379  }
   380  
   381  // AddSelfUpdate implements the op interface for createOp -- see the
   382  // comment in op.
   383  func (co *createOp) AddSelfUpdate(ptr data.BlockPointer) {
   384  	co.AddUpdate(ptr, ptr)
   385  }
   386  
   387  func (co *createOp) SizeExceptUpdates() uint64 {
   388  	return uint64(len(co.NewName))
   389  }
   390  
   391  func (co *createOp) allUpdates() []blockUpdate {
   392  	updates := make([]blockUpdate, len(co.Updates))
   393  	copy(updates, co.Updates)
   394  	return append(updates, co.Dir)
   395  }
   396  
   397  func (co *createOp) checkValid() error {
   398  	if co.NewName == "" {
   399  		// Must be for root dir.
   400  		return nil
   401  	}
   402  
   403  	err := co.Dir.checkValid()
   404  	if err != nil {
   405  		return errors.Errorf("createOp.Dir=%v got error: %v", co.Dir, err)
   406  	}
   407  	return co.checkUpdatesValid()
   408  }
   409  
   410  func (co *createOp) obfuscatedNewName() data.PathPartString {
   411  	return co.obfuscatedName(co.NewName)
   412  }
   413  
   414  func (co *createOp) String() string {
   415  	res := fmt.Sprintf("create %s (%s)", co.obfuscatedNewName(), co.Type)
   416  	if co.renamed {
   417  		res += " (renamed)"
   418  	}
   419  	return res
   420  }
   421  
   422  func (co *createOp) Plaintext() string {
   423  	res := fmt.Sprintf("create %s (%s)", co.NewName, co.Type)
   424  	if co.renamed {
   425  		res += " (renamed)"
   426  	}
   427  	return res
   428  }
   429  
   430  func (co *createOp) StringWithRefs(indent string) string {
   431  	res := co.String() + "\n"
   432  	res += indent + fmt.Sprintf("Dir: %v -> %v\n", co.Dir.Unref, co.Dir.Ref)
   433  	res += co.stringWithRefs(indent)
   434  	return res
   435  }
   436  
   437  func (co *createOp) checkConflict(
   438  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
   439  	isFile bool) (crAction, error) {
   440  	if realMergedOp, ok := mergedOp.(*createOp); ok {
   441  		// Conflicts if this creates the same name and one of them
   442  		// isn't creating a directory.
   443  		sameName := (realMergedOp.NewName == co.NewName)
   444  		if sameName && (realMergedOp.Type != data.Dir || co.Type != data.Dir) {
   445  			if realMergedOp.Type != data.Dir &&
   446  				(co.Type == data.Dir || co.crSymPath != "") {
   447  				// Rename the merged entry only if the unmerged one is
   448  				// a directory (or to-be-sympath'd directory) and the
   449  				// merged one is not.
   450  				toName, err := renamer.ConflictRename(
   451  					ctx, mergedOp, co.NewName)
   452  				if err != nil {
   453  					return nil, err
   454  				}
   455  				return &renameMergedAction{
   456  					fromName: co.obfuscatedNewName(),
   457  					toName:   co.obfuscatedName(toName),
   458  					symPath:  co.obfuscatedName(co.crSymPath),
   459  				}, nil
   460  			}
   461  			// Otherwise rename the unmerged entry (guaranteed to be a file).
   462  			toName, err := renamer.ConflictRename(
   463  				ctx, co, co.NewName)
   464  			if err != nil {
   465  				return nil, err
   466  			}
   467  			return &renameUnmergedAction{
   468  				fromName: co.obfuscatedNewName(),
   469  				toName:   co.obfuscatedName(toName),
   470  				symPath:  co.obfuscatedName(co.crSymPath),
   471  			}, nil
   472  		}
   473  
   474  		// If they are both directories, and one of them is a rename,
   475  		// then we have a conflict and need to rename the renamed one.
   476  		//
   477  		// TODO: Implement a better merging strategy for when an
   478  		// existing directory gets into a rename conflict with another
   479  		// existing or new directory.
   480  		if sameName && realMergedOp.Type == data.Dir && co.Type == data.Dir &&
   481  			(realMergedOp.renamed || co.renamed) {
   482  			// Always rename the unmerged one
   483  			toName, err := renamer.ConflictRename(
   484  				ctx, co, co.NewName)
   485  			if err != nil {
   486  				return nil, err
   487  			}
   488  			return &copyUnmergedEntryAction{
   489  				fromName: co.obfuscatedNewName(),
   490  				toName:   co.obfuscatedName(toName),
   491  				symPath:  co.obfuscatedName(co.crSymPath),
   492  				unique:   true,
   493  			}, nil
   494  		}
   495  	}
   496  	// Doesn't conflict with any rmOps, because the default action
   497  	// will just re-create it in the merged branch as necessary.
   498  	return nil, nil
   499  }
   500  
   501  func (co *createOp) getDefaultAction(mergedPath data.Path) crAction {
   502  	newName := co.obfuscatedNewName()
   503  	if co.forceCopy {
   504  		return &renameUnmergedAction{
   505  			fromName: newName,
   506  			toName:   newName,
   507  			symPath:  co.obfuscatedName(co.crSymPath),
   508  		}
   509  	}
   510  	return &copyUnmergedEntryAction{
   511  		fromName: newName,
   512  		toName:   newName,
   513  		symPath:  co.obfuscatedName(co.crSymPath),
   514  	}
   515  }
   516  
   517  func makeBaseEditNotification(
   518  	rev kbfsmd.Revision, revTime time.Time, device kbfscrypto.VerifyingKey,
   519  	uid keybase1.UID, tlfID tlf.ID,
   520  	et data.EntryType) kbfsedits.NotificationMessage {
   521  	var t kbfsedits.EntryType
   522  	switch et {
   523  	case data.File, data.Exec:
   524  		t = kbfsedits.EntryTypeFile
   525  	case data.Dir:
   526  		t = kbfsedits.EntryTypeDir
   527  	case data.Sym:
   528  		t = kbfsedits.EntryTypeSym
   529  	}
   530  	return kbfsedits.NotificationMessage{
   531  		Version:  kbfsedits.NotificationV2,
   532  		FileType: t,
   533  		Time:     revTime,
   534  		Revision: rev,
   535  		Device:   device,
   536  		UID:      uid,
   537  		FolderID: tlfID,
   538  	}
   539  }
   540  
   541  func (co *createOp) ToEditNotification(
   542  	rev kbfsmd.Revision, revTime time.Time, device kbfscrypto.VerifyingKey,
   543  	uid keybase1.UID, tlfID tlf.ID) *kbfsedits.NotificationMessage {
   544  	n := makeBaseEditNotification(rev, revTime, device, uid, tlfID, co.Type)
   545  	n.Filename = co.getFinalPath().ChildPathNoPtr(co.obfuscatedNewName(), nil).
   546  		CanonicalPathPlaintext()
   547  	n.Type = kbfsedits.NotificationCreate
   548  	return &n
   549  }
   550  
   551  // rmOp is an op representing a file or subdirectory removal
   552  type rmOp struct {
   553  	OpCommon
   554  	OldName     string         `codec:"n"`
   555  	Dir         blockUpdate    `codec:"d"`
   556  	RemovedType data.EntryType `codec:"rt"`
   557  
   558  	// Indicates that the resolution process should skip this rm op.
   559  	// Likely indicates the rm half of a cycle-creating rename.
   560  	dropThis bool
   561  }
   562  
   563  func newRmOp(name string, oldDir data.BlockPointer, removedType data.EntryType) (
   564  	*rmOp, error) {
   565  	ro := &rmOp{
   566  		OldName:     name,
   567  		RemovedType: removedType,
   568  	}
   569  	err := ro.Dir.setUnref(oldDir)
   570  	if err != nil {
   571  		return nil, err
   572  	}
   573  	return ro, nil
   574  }
   575  
   576  func (ro *rmOp) deepCopy() op {
   577  	roCopy := *ro
   578  	roCopy.OpCommon = ro.OpCommon.deepCopy()
   579  	return &roCopy
   580  }
   581  
   582  func (ro *rmOp) AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer) {
   583  	if oldPtr == ro.Dir.Unref {
   584  		err := ro.Dir.setRef(newPtr)
   585  		if err != nil {
   586  			panic(err)
   587  		}
   588  		return
   589  	}
   590  	ro.OpCommon.AddUpdate(oldPtr, newPtr)
   591  }
   592  
   593  // AddSelfUpdate implements the op interface for rmOp -- see the
   594  // comment in op.
   595  func (ro *rmOp) AddSelfUpdate(ptr data.BlockPointer) {
   596  	ro.AddUpdate(ptr, ptr)
   597  }
   598  
   599  func (ro *rmOp) SizeExceptUpdates() uint64 {
   600  	return uint64(len(ro.OldName))
   601  }
   602  
   603  func (ro *rmOp) allUpdates() []blockUpdate {
   604  	updates := make([]blockUpdate, len(ro.Updates))
   605  	copy(updates, ro.Updates)
   606  	return append(updates, ro.Dir)
   607  }
   608  
   609  func (ro *rmOp) checkValid() error {
   610  	err := ro.Dir.checkValid()
   611  	if err != nil {
   612  		return errors.Errorf("rmOp.Dir=%v got error: %v", ro.Dir, err)
   613  	}
   614  	return ro.checkUpdatesValid()
   615  }
   616  
   617  func (ro *rmOp) obfuscatedOldName() data.PathPartString {
   618  	return ro.obfuscatedName(ro.OldName)
   619  }
   620  
   621  func (ro *rmOp) String() string {
   622  	return fmt.Sprintf("rm %s", ro.obfuscatedOldName())
   623  }
   624  
   625  func (ro *rmOp) Plaintext() string {
   626  	return fmt.Sprintf("rm %s", ro.OldName)
   627  }
   628  
   629  func (ro *rmOp) StringWithRefs(indent string) string {
   630  	res := ro.String() + "\n"
   631  	res += indent + fmt.Sprintf("Dir: %v -> %v\n", ro.Dir.Unref, ro.Dir.Ref)
   632  	res += ro.stringWithRefs(indent)
   633  	return res
   634  }
   635  
   636  func (ro *rmOp) checkConflict(
   637  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
   638  	isFile bool) (crAction, error) {
   639  	switch realMergedOp := mergedOp.(type) {
   640  	case *createOp:
   641  		if realMergedOp.NewName == ro.OldName {
   642  			// Conflicts if this creates the same name.  This can only
   643  			// happen if the merged branch deleted the old node and
   644  			// re-created it, in which case it is totally fine to drop
   645  			// this rm op for the original node.
   646  			return &dropUnmergedAction{op: ro}, nil
   647  		}
   648  	case *rmOp:
   649  		if realMergedOp.OldName == ro.OldName {
   650  			// Both removed the same file.
   651  			return &dropUnmergedAction{op: ro}, nil
   652  		}
   653  	}
   654  	return nil, nil
   655  }
   656  
   657  func (ro *rmOp) getDefaultAction(mergedPath data.Path) crAction {
   658  	if ro.dropThis {
   659  		return &dropUnmergedAction{op: ro}
   660  	}
   661  	return &rmMergedEntryAction{name: ro.obfuscatedOldName()}
   662  }
   663  
   664  func (ro *rmOp) ToEditNotification(
   665  	rev kbfsmd.Revision, revTime time.Time, device kbfscrypto.VerifyingKey,
   666  	uid keybase1.UID, tlfID tlf.ID) *kbfsedits.NotificationMessage {
   667  	n := makeBaseEditNotification(
   668  		rev, revTime, device, uid, tlfID, ro.RemovedType)
   669  	n.Filename = ro.getFinalPath().ChildPathNoPtr(ro.obfuscatedOldName(), nil).
   670  		CanonicalPathPlaintext()
   671  	n.Type = kbfsedits.NotificationDelete
   672  	return &n
   673  }
   674  
   675  // renameOp is an op representing a rename of a file/subdirectory from
   676  // one directory to another.  If this is a rename within the same
   677  // directory, NewDir will be equivalent to blockUpdate{}.  renameOp
   678  // records the moved pointer, even though it doesn't change as part of
   679  // the operation, to make it possible to track the full path of
   680  // directories for the purposes of conflict resolution.
   681  type renameOp struct {
   682  	OpCommon
   683  	OldName     string            `codec:"on"`
   684  	OldDir      blockUpdate       `codec:"od"`
   685  	NewName     string            `codec:"nn"`
   686  	NewDir      blockUpdate       `codec:"nd"`
   687  	Renamed     data.BlockPointer `codec:"re"`
   688  	RenamedType data.EntryType    `codec:"rt"`
   689  
   690  	// oldFinalPath is the final resolved path to the old directory
   691  	// containing the renamed node.  Not exported; only used locally.
   692  	oldFinalPath data.Path
   693  }
   694  
   695  func newRenameOp(oldName string, oldOldDir data.BlockPointer,
   696  	newName string, oldNewDir data.BlockPointer, renamed data.BlockPointer,
   697  	renamedType data.EntryType) (*renameOp, error) {
   698  	ro := &renameOp{
   699  		OldName:     oldName,
   700  		NewName:     newName,
   701  		Renamed:     renamed,
   702  		RenamedType: renamedType,
   703  	}
   704  	err := ro.OldDir.setUnref(oldOldDir)
   705  	if err != nil {
   706  		return nil, err
   707  	}
   708  	// If we are renaming within a directory, let the NewDir remain empty.
   709  	if oldOldDir != oldNewDir {
   710  		err := ro.NewDir.setUnref(oldNewDir)
   711  		if err != nil {
   712  			return nil, err
   713  		}
   714  	}
   715  	return ro, nil
   716  }
   717  
   718  func (ro *renameOp) deepCopy() op {
   719  	roCopy := *ro
   720  	roCopy.OpCommon = ro.OpCommon.deepCopy()
   721  	return &roCopy
   722  }
   723  
   724  func (ro *renameOp) AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer) {
   725  	if oldPtr == ro.OldDir.Unref {
   726  		err := ro.OldDir.setRef(newPtr)
   727  		if err != nil {
   728  			panic(err)
   729  		}
   730  		return
   731  	}
   732  	if ro.NewDir != (blockUpdate{}) && oldPtr == ro.NewDir.Unref {
   733  		err := ro.NewDir.setRef(newPtr)
   734  		if err != nil {
   735  			panic(err)
   736  		}
   737  		return
   738  	}
   739  	ro.OpCommon.AddUpdate(oldPtr, newPtr)
   740  }
   741  
   742  // AddSelfUpdate implements the op interface for renameOp -- see the
   743  // comment in op.
   744  func (ro *renameOp) AddSelfUpdate(ptr data.BlockPointer) {
   745  	ro.AddUpdate(ptr, ptr)
   746  }
   747  
   748  func (ro *renameOp) SizeExceptUpdates() uint64 {
   749  	return uint64(len(ro.NewName) + len(ro.NewName))
   750  }
   751  
   752  func (ro *renameOp) allUpdates() []blockUpdate {
   753  	updates := make([]blockUpdate, len(ro.Updates))
   754  	copy(updates, ro.Updates)
   755  	if ro.NewDir != (blockUpdate{}) {
   756  		return append(updates, ro.NewDir, ro.OldDir)
   757  	}
   758  	return append(updates, ro.OldDir)
   759  }
   760  
   761  func (ro *renameOp) checkValid() error {
   762  	err := ro.OldDir.checkValid()
   763  	if err != nil {
   764  		return errors.Errorf("renameOp.OldDir=%v got error: %v",
   765  			ro.OldDir, err)
   766  	}
   767  	if ro.NewDir != (blockUpdate{}) {
   768  		err = ro.NewDir.checkValid()
   769  		if err != nil {
   770  			return errors.Errorf("renameOp.NewDir=%v got error: %v",
   771  				ro.NewDir, err)
   772  		}
   773  	}
   774  
   775  	return ro.checkUpdatesValid()
   776  }
   777  
   778  func (ro *renameOp) obfuscatedOldName() data.PathPartString {
   779  	return ro.obfuscatedName(ro.OldName)
   780  }
   781  
   782  func (ro *renameOp) obfuscatedNewName() data.PathPartString {
   783  	return ro.obfuscatedName(ro.NewName)
   784  }
   785  
   786  func (ro *renameOp) String() string {
   787  	return fmt.Sprintf("rename %s -> %s (%s)",
   788  		ro.obfuscatedOldName(), ro.obfuscatedNewName(), ro.RenamedType)
   789  }
   790  
   791  func (ro *renameOp) Plaintext() string {
   792  	return fmt.Sprintf("rename %s -> %s (%s)",
   793  		ro.OldName, ro.NewName, ro.RenamedType)
   794  }
   795  
   796  func (ro *renameOp) StringWithRefs(indent string) string {
   797  	res := ro.String() + "\n"
   798  	res += indent + fmt.Sprintf("OldDir: %v -> %v\n",
   799  		ro.OldDir.Unref, ro.OldDir.Ref)
   800  	if ro.NewDir != (blockUpdate{}) {
   801  		res += indent + fmt.Sprintf("NewDir: %v -> %v\n",
   802  			ro.NewDir.Unref, ro.NewDir.Ref)
   803  	} else {
   804  		res += indent + "NewDir: same as above\n"
   805  	}
   806  	res += indent + fmt.Sprintf("Renamed: %v\n", ro.Renamed)
   807  	res += ro.stringWithRefs(indent)
   808  	return res
   809  }
   810  
   811  func (ro *renameOp) checkConflict(
   812  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
   813  	isFile bool) (crAction, error) {
   814  	return nil, errors.Errorf("Unexpected conflict check on a rename op: %s", ro)
   815  }
   816  
   817  func (ro *renameOp) getDefaultAction(mergedPath data.Path) crAction {
   818  	return nil
   819  }
   820  
   821  func (ro *renameOp) ToEditNotification(
   822  	rev kbfsmd.Revision, revTime time.Time, device kbfscrypto.VerifyingKey,
   823  	uid keybase1.UID, tlfID tlf.ID) *kbfsedits.NotificationMessage {
   824  	n := makeBaseEditNotification(
   825  		rev, revTime, device, uid, tlfID, ro.RenamedType)
   826  	n.Filename = ro.getFinalPath().ChildPathNoPtr(ro.obfuscatedNewName(), nil).
   827  		CanonicalPathPlaintext()
   828  	n.Type = kbfsedits.NotificationRename
   829  	n.Params = &kbfsedits.NotificationParams{
   830  		OldFilename: ro.oldFinalPath.ChildPathNoPtr(
   831  			ro.obfuscatedOldName(), nil).CanonicalPathPlaintext(),
   832  	}
   833  	return &n
   834  }
   835  
   836  // WriteRange represents a file modification.  Len is 0 for a
   837  // truncate.
   838  type WriteRange struct {
   839  	Off uint64 `codec:"o"`
   840  	Len uint64 `codec:"l,omitempty"` // 0 for truncates
   841  
   842  	codec.UnknownFieldSetHandler
   843  }
   844  
   845  func (w WriteRange) isTruncate() bool {
   846  	return w.Len == 0
   847  }
   848  
   849  // End returns the index of the largest byte not affected by this
   850  // write.  It only makes sense to call this for non-truncates.
   851  func (w WriteRange) End() uint64 {
   852  	if w.isTruncate() {
   853  		panic("Truncates don't have an end")
   854  	}
   855  	return w.Off + w.Len
   856  }
   857  
   858  // Affects returns true if the regions affected by this write
   859  // operation and `other` overlap in some way.  Specifically, it
   860  // returns true if:
   861  //
   862  //   - both operations are writes and their write ranges overlap;
   863  //   - one operation is a write and one is a truncate, and the truncate is
   864  //     within the write's range or before it; or
   865  //   - both operations are truncates.
   866  func (w WriteRange) Affects(other WriteRange) bool {
   867  	if w.isTruncate() {
   868  		if other.isTruncate() {
   869  			return true
   870  		}
   871  		// A truncate affects a write if it lands inside or before the
   872  		// write.
   873  		return other.End() > w.Off
   874  	} else if other.isTruncate() {
   875  		return w.End() > other.Off
   876  	}
   877  	// Both are writes -- do their ranges overlap?
   878  	return (w.Off <= other.End() && other.End() <= w.End()) ||
   879  		(other.Off <= w.End() && w.End() <= other.End())
   880  }
   881  
   882  // syncOp is an op that represents a series of writes to a file.
   883  type syncOp struct {
   884  	OpCommon
   885  	File   blockUpdate  `codec:"f"`
   886  	Writes []WriteRange `codec:"w"`
   887  
   888  	// If true, this says that if there is a conflict involving this
   889  	// op, we should keep the unmerged name rather than construct a
   890  	// conflict name (probably because the new name already
   891  	// diverges from the name in the other branch).
   892  	keepUnmergedTailName bool
   893  }
   894  
   895  func newSyncOp(oldFile data.BlockPointer) (*syncOp, error) {
   896  	so := &syncOp{}
   897  	err := so.File.setUnref(oldFile)
   898  	if err != nil {
   899  		return nil, err
   900  	}
   901  	so.resetUpdateState()
   902  	return so, nil
   903  }
   904  
   905  func (so *syncOp) deepCopy() op {
   906  	soCopy := *so
   907  	soCopy.OpCommon = so.OpCommon.deepCopy()
   908  	soCopy.Writes = make([]WriteRange, len(so.Writes))
   909  	copy(soCopy.Writes, so.Writes)
   910  	return &soCopy
   911  }
   912  
   913  func (so *syncOp) resetUpdateState() {
   914  	so.Updates = nil
   915  }
   916  
   917  func (so *syncOp) AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer) {
   918  	if oldPtr == so.File.Unref {
   919  		err := so.File.setRef(newPtr)
   920  		if err != nil {
   921  			panic(err)
   922  		}
   923  		return
   924  	}
   925  	so.OpCommon.AddUpdate(oldPtr, newPtr)
   926  }
   927  
   928  // AddSelfUpdate implements the op interface for syncOp -- see the
   929  // comment in op.
   930  func (so *syncOp) AddSelfUpdate(ptr data.BlockPointer) {
   931  	so.AddUpdate(ptr, ptr)
   932  }
   933  
   934  func (so *syncOp) addWrite(off uint64, length uint64) WriteRange {
   935  	latestWrite := WriteRange{Off: off, Len: length}
   936  	so.Writes = append(so.Writes, latestWrite)
   937  	return latestWrite
   938  }
   939  
   940  func (so *syncOp) addTruncate(off uint64) WriteRange {
   941  	latestWrite := WriteRange{Off: off, Len: 0}
   942  	so.Writes = append(so.Writes, latestWrite)
   943  	return latestWrite
   944  }
   945  
   946  func (so *syncOp) SizeExceptUpdates() uint64 {
   947  	return uint64(len(so.Writes) * 16)
   948  }
   949  
   950  func (so *syncOp) allUpdates() []blockUpdate {
   951  	updates := make([]blockUpdate, len(so.Updates))
   952  	copy(updates, so.Updates)
   953  	return append(updates, so.File)
   954  }
   955  
   956  func (so *syncOp) checkValid() error {
   957  	err := so.File.checkValid()
   958  	if err != nil {
   959  		return errors.Errorf("syncOp.File=%v got error: %v", so.File, err)
   960  	}
   961  	return so.checkUpdatesValid()
   962  }
   963  
   964  func (so *syncOp) String() string {
   965  	var writes []string
   966  	for _, r := range so.Writes {
   967  		writes = append(writes, fmt.Sprintf("{off=%d, len=%d}", r.Off, r.Len))
   968  	}
   969  	return fmt.Sprintf("sync [%s]", strings.Join(writes, ", "))
   970  }
   971  
   972  func (so *syncOp) Plaintext() string {
   973  	return so.String()
   974  }
   975  
   976  func (so *syncOp) StringWithRefs(indent string) string {
   977  	res := so.String() + "\n"
   978  	res += indent + fmt.Sprintf("File: %v -> %v\n", so.File.Unref, so.File.Ref)
   979  	res += so.stringWithRefs(indent)
   980  	return res
   981  }
   982  
   983  func (so *syncOp) checkConflict(
   984  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
   985  	isFile bool) (crAction, error) {
   986  	switch mergedOp.(type) {
   987  	case *syncOp:
   988  		// Any sync on the same file is a conflict.  (TODO: add
   989  		// type-specific intelligent conflict resolvers for file
   990  		// contents?)
   991  		toName, err := renamer.ConflictRename(
   992  			ctx, so, mergedOp.getFinalPath().TailName().Plaintext())
   993  		if err != nil {
   994  			return nil, err
   995  		}
   996  
   997  		if so.keepUnmergedTailName {
   998  			toName = so.getFinalPath().TailName().Plaintext()
   999  		}
  1000  
  1001  		return &renameUnmergedAction{
  1002  			fromName:                 so.getFinalPath().TailName(),
  1003  			toName:                   so.obfuscatedName(toName),
  1004  			unmergedParentMostRecent: so.getFinalPath().ParentPath().TailPointer(),
  1005  			mergedParentMostRecent: mergedOp.getFinalPath().ParentPath().
  1006  				TailPointer(),
  1007  		}, nil
  1008  	case *setAttrOp:
  1009  		// Someone on the merged path explicitly set an attribute, so
  1010  		// just copy the size and blockpointer over.
  1011  		return &copyUnmergedAttrAction{
  1012  			fromName: so.getFinalPath().TailName(),
  1013  			toName:   mergedOp.getFinalPath().TailName(),
  1014  			attr:     []attrChange{sizeAttr},
  1015  		}, nil
  1016  	}
  1017  	return nil, nil
  1018  }
  1019  
  1020  func (so *syncOp) getDefaultAction(mergedPath data.Path) crAction {
  1021  	return &copyUnmergedEntryAction{
  1022  		fromName: so.getFinalPath().TailName(),
  1023  		toName:   mergedPath.TailName(),
  1024  		symPath:  so.obfuscatedName(""),
  1025  	}
  1026  }
  1027  
  1028  func (so *syncOp) ToEditNotification(
  1029  	rev kbfsmd.Revision, revTime time.Time, device kbfscrypto.VerifyingKey,
  1030  	uid keybase1.UID, tlfID tlf.ID) *kbfsedits.NotificationMessage {
  1031  	n := makeBaseEditNotification(rev, revTime, device, uid, tlfID, data.File)
  1032  	n.Filename = so.getFinalPath().CanonicalPathPlaintext()
  1033  	n.Type = kbfsedits.NotificationModify
  1034  	var mods []kbfsedits.ModifyRange
  1035  	for _, w := range so.Writes {
  1036  		mods = append(mods, kbfsedits.ModifyRange{
  1037  			Offset: w.Off,
  1038  			Length: w.Len,
  1039  		})
  1040  	}
  1041  	n.Params = &kbfsedits.NotificationParams{
  1042  		Modifies: mods,
  1043  	}
  1044  	return &n
  1045  }
  1046  
  1047  // In the functions below. a collapsed []WriteRange is a sequence of
  1048  // non-overlapping writes with strictly increasing Off, and maybe a
  1049  // trailing truncate (with strictly greater Off).
  1050  
  1051  // coalesceWrites combines the given `wNew` with the head and tail of
  1052  // the given collapsed `existingWrites` slice.  For example, if the
  1053  // new write is {5, 100}, and `existingWrites` = [{7,5}, {18,10},
  1054  // {98,10}], the returned write will be {5,103}.  There may be a
  1055  // truncate at the end of the returned slice as well.
  1056  func coalesceWrites(existingWrites []WriteRange, wNew WriteRange) []WriteRange {
  1057  	if wNew.isTruncate() {
  1058  		panic("coalesceWrites cannot be called with a new truncate.")
  1059  	}
  1060  	if len(existingWrites) == 0 {
  1061  		return []WriteRange{wNew}
  1062  	}
  1063  	newOff := wNew.Off
  1064  	newEnd := wNew.End()
  1065  	wOldHead := existingWrites[0]
  1066  	wOldTail := existingWrites[len(existingWrites)-1]
  1067  	if !wOldTail.isTruncate() && wOldTail.End() > newEnd {
  1068  		newEnd = wOldTail.End()
  1069  	}
  1070  	if !wOldHead.isTruncate() && wOldHead.Off < newOff {
  1071  		newOff = wOldHead.Off
  1072  	}
  1073  	ret := []WriteRange{{Off: newOff, Len: newEnd - newOff}}
  1074  	if wOldTail.isTruncate() {
  1075  		ret = append(ret, WriteRange{Off: newEnd})
  1076  	}
  1077  	return ret
  1078  }
  1079  
  1080  // Assumes writes is already collapsed, i.e. a sequence of
  1081  // non-overlapping writes with strictly increasing Off, and maybe a
  1082  // trailing truncate (with strictly greater Off).
  1083  func addToCollapsedWriteRange(writes []WriteRange,
  1084  	wNew WriteRange) []WriteRange {
  1085  	// Form three regions: head, mid, and tail: head is the maximal prefix
  1086  	// of writes less than (with respect to Off) and unaffected by wNew,
  1087  	// tail is the maximal suffix of writes greater than (with respect to
  1088  	// Off) and unaffected by wNew, and mid is everything else, i.e. the
  1089  	// range of writes affected by wNew.
  1090  	var headEnd int
  1091  	for ; headEnd < len(writes); headEnd++ {
  1092  		wOld := writes[headEnd]
  1093  		if wOld.Off >= wNew.Off || wNew.Affects(wOld) {
  1094  			break
  1095  		}
  1096  	}
  1097  	head := writes[:headEnd]
  1098  
  1099  	if wNew.isTruncate() {
  1100  		// end is empty, since a truncate affects a suffix of writes.
  1101  		mid := writes[headEnd:]
  1102  
  1103  		switch {
  1104  		case len(mid) == 0:
  1105  			// Truncate past the last write.
  1106  			return append(head, wNew)
  1107  		case mid[0].isTruncate():
  1108  			if mid[0].Off < wNew.Off {
  1109  				// A larger new truncate causes zero-fill.
  1110  				zeroLen := wNew.Off - mid[0].Off
  1111  				if len(head) > 0 {
  1112  					lastHead := head[len(head)-1]
  1113  					if lastHead.Off+lastHead.Len == mid[0].Off {
  1114  						// Combine this zero-fill with the previous write.
  1115  						head[len(head)-1].Len += zeroLen
  1116  						return append(head, wNew)
  1117  					}
  1118  				}
  1119  				return append(head,
  1120  					WriteRange{Off: mid[0].Off, Len: zeroLen}, wNew)
  1121  			}
  1122  			return append(head, wNew)
  1123  		case mid[0].Off < wNew.Off:
  1124  			return append(head, WriteRange{
  1125  				Off: mid[0].Off,
  1126  				Len: wNew.Off - mid[0].Off,
  1127  			}, wNew)
  1128  		}
  1129  		return append(head, wNew)
  1130  	}
  1131  
  1132  	// wNew is a write.
  1133  
  1134  	midEnd := headEnd
  1135  	for ; midEnd < len(writes); midEnd++ {
  1136  		wOld := writes[midEnd]
  1137  		if !wNew.Affects(wOld) {
  1138  			break
  1139  		}
  1140  	}
  1141  
  1142  	mid := writes[headEnd:midEnd]
  1143  	end := writes[midEnd:]
  1144  	mid = coalesceWrites(mid, wNew)
  1145  	return append(head, append(mid, end...)...)
  1146  }
  1147  
  1148  // collapseWriteRange returns a set of writes that represent the final
  1149  // dirty state of this file after this syncOp, given a previous write
  1150  // range.  It coalesces overlapping dirty writes, and it erases any
  1151  // writes that occurred before a truncation with an offset smaller
  1152  // than its max dirty byte.
  1153  //
  1154  // This function assumes that `writes` has already been collapsed (or
  1155  // is nil).
  1156  //
  1157  // NOTE: Truncates past a file's end get turned into writes by
  1158  // folderBranchOps, but in the future we may have bona fide truncate
  1159  // WriteRanges past a file's end.
  1160  func (so *syncOp) collapseWriteRange(writes []WriteRange) (
  1161  	newWrites []WriteRange) {
  1162  	newWrites = writes
  1163  	for _, wNew := range so.Writes {
  1164  		newWrites = addToCollapsedWriteRange(newWrites, wNew)
  1165  	}
  1166  	return newWrites
  1167  }
  1168  
  1169  type attrChange uint16
  1170  
  1171  const (
  1172  	exAttr attrChange = iota
  1173  	mtimeAttr
  1174  	sizeAttr // only used during conflict resolution
  1175  )
  1176  
  1177  func (ac attrChange) String() string {
  1178  	switch ac {
  1179  	case exAttr:
  1180  		return "ex"
  1181  	case mtimeAttr:
  1182  		return "mtime"
  1183  	case sizeAttr:
  1184  		return "size"
  1185  	}
  1186  	return "<invalid attrChange>"
  1187  }
  1188  
  1189  // setAttrOp is an op that represents changing the attributes of a
  1190  // file/subdirectory with in a directory.
  1191  type setAttrOp struct {
  1192  	OpCommon
  1193  	Name string            `codec:"n"`
  1194  	Dir  blockUpdate       `codec:"d"`
  1195  	Attr attrChange        `codec:"a"`
  1196  	File data.BlockPointer `codec:"f"`
  1197  
  1198  	// If true, this says that if there is a conflict involving this
  1199  	// op, we should keep the unmerged name rather than construct a
  1200  	// conflict name (probably because the new name already
  1201  	// diverges from the name in the other branch).
  1202  	keepUnmergedTailName bool
  1203  }
  1204  
  1205  func newSetAttrOp(name string, oldDir data.BlockPointer,
  1206  	attr attrChange, file data.BlockPointer) (*setAttrOp, error) {
  1207  	sao := &setAttrOp{
  1208  		Name: name,
  1209  	}
  1210  	err := sao.Dir.setUnref(oldDir)
  1211  	if err != nil {
  1212  		return nil, err
  1213  	}
  1214  	sao.Attr = attr
  1215  	sao.File = file
  1216  	return sao, nil
  1217  }
  1218  
  1219  func (sao *setAttrOp) deepCopy() op {
  1220  	saoCopy := *sao
  1221  	saoCopy.OpCommon = sao.OpCommon.deepCopy()
  1222  	return &saoCopy
  1223  }
  1224  
  1225  func (sao *setAttrOp) AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer) {
  1226  	if oldPtr == sao.Dir.Unref {
  1227  		err := sao.Dir.setRef(newPtr)
  1228  		if err != nil {
  1229  			panic(err)
  1230  		}
  1231  		return
  1232  	}
  1233  	sao.OpCommon.AddUpdate(oldPtr, newPtr)
  1234  }
  1235  
  1236  // AddSelfUpdate implements the op interface for setAttrOp -- see the
  1237  // comment in op.
  1238  func (sao *setAttrOp) AddSelfUpdate(ptr data.BlockPointer) {
  1239  	sao.AddUpdate(ptr, ptr)
  1240  }
  1241  
  1242  func (sao *setAttrOp) SizeExceptUpdates() uint64 {
  1243  	return uint64(len(sao.Name))
  1244  }
  1245  
  1246  func (sao *setAttrOp) allUpdates() []blockUpdate {
  1247  	updates := make([]blockUpdate, len(sao.Updates))
  1248  	copy(updates, sao.Updates)
  1249  	return append(updates, sao.Dir)
  1250  }
  1251  
  1252  func (sao *setAttrOp) checkValid() error {
  1253  	err := sao.Dir.checkValid()
  1254  	if err != nil {
  1255  		return errors.Errorf("setAttrOp.Dir=%v got error: %v", sao.Dir, err)
  1256  	}
  1257  	return sao.checkUpdatesValid()
  1258  }
  1259  
  1260  func (sao *setAttrOp) obfuscatedEntryName() data.PathPartString {
  1261  	return data.NewPathPartString(
  1262  		sao.Name, sao.finalPath.ParentPath().Obfuscator())
  1263  }
  1264  
  1265  func (sao *setAttrOp) String() string {
  1266  	return fmt.Sprintf("setAttr %s (%s)", sao.obfuscatedEntryName(), sao.Attr)
  1267  }
  1268  
  1269  func (sao *setAttrOp) Plaintext() string {
  1270  	return fmt.Sprintf("setAttr %s (%s)", sao.Name, sao.Attr)
  1271  }
  1272  
  1273  func (sao *setAttrOp) StringWithRefs(indent string) string {
  1274  	res := sao.String() + "\n"
  1275  	res += indent + fmt.Sprintf("Dir: %v -> %v\n", sao.Dir.Unref, sao.Dir.Ref)
  1276  	res += indent + fmt.Sprintf("File: %v\n", sao.File)
  1277  	res += sao.stringWithRefs(indent)
  1278  	return res
  1279  }
  1280  
  1281  func (sao *setAttrOp) checkConflict(
  1282  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
  1283  	isFile bool) (crAction, error) {
  1284  	if realMergedOp, ok := mergedOp.(*setAttrOp); ok &&
  1285  		realMergedOp.Attr == sao.Attr {
  1286  		var symPath string
  1287  		var causedByAttr attrChange
  1288  		if !isFile {
  1289  			// A directory has a conflict on an mtime attribute.
  1290  			// Create a symlink entry with the unmerged mtime
  1291  			// pointing to the merged entry.
  1292  			symPath = mergedOp.getFinalPath().TailName().Plaintext()
  1293  			causedByAttr = sao.Attr
  1294  		}
  1295  
  1296  		// A set attr for the same attribute on the same file is a
  1297  		// conflict.
  1298  		fromName := sao.getFinalPath().TailName()
  1299  		toName, err := renamer.ConflictRename(
  1300  			ctx, sao, fromName.Plaintext())
  1301  		if err != nil {
  1302  			return nil, err
  1303  		}
  1304  
  1305  		if sao.keepUnmergedTailName {
  1306  			toName = sao.getFinalPath().TailName().Plaintext()
  1307  		}
  1308  
  1309  		toNamePPS := data.NewPathPartString(
  1310  			toName, sao.finalPath.ParentPath().Obfuscator())
  1311  		return &renameUnmergedAction{
  1312  			fromName:                 fromName,
  1313  			toName:                   toNamePPS,
  1314  			symPath:                  sao.obfuscatedName(symPath),
  1315  			causedByAttr:             causedByAttr,
  1316  			unmergedParentMostRecent: sao.getFinalPath().ParentPath().TailPointer(),
  1317  			mergedParentMostRecent: mergedOp.getFinalPath().ParentPath().
  1318  				TailPointer(),
  1319  		}, nil
  1320  	}
  1321  	return nil, nil
  1322  }
  1323  
  1324  func (sao *setAttrOp) getDefaultAction(mergedPath data.Path) crAction {
  1325  	return &copyUnmergedAttrAction{
  1326  		fromName: sao.getFinalPath().TailName(),
  1327  		toName:   mergedPath.TailName(),
  1328  		attr:     []attrChange{sao.Attr},
  1329  	}
  1330  }
  1331  
  1332  // resolutionOp is an op that represents the block changes that took
  1333  // place as part of a conflict resolution.
  1334  type resolutionOp struct {
  1335  	OpCommon
  1336  	UncommittedUnrefs []data.BlockPointer `codec:"uu"`
  1337  }
  1338  
  1339  func newResolutionOp() *resolutionOp {
  1340  	ro := &resolutionOp{}
  1341  	return ro
  1342  }
  1343  
  1344  func (ro *resolutionOp) deepCopy() op {
  1345  	roCopy := *ro
  1346  	roCopy.OpCommon = ro.OpCommon.deepCopy()
  1347  	roCopy.UncommittedUnrefs = make([]data.BlockPointer, len(ro.UncommittedUnrefs))
  1348  	copy(roCopy.UncommittedUnrefs, ro.UncommittedUnrefs)
  1349  	return &roCopy
  1350  }
  1351  
  1352  func (ro *resolutionOp) Unrefs() []data.BlockPointer {
  1353  	return append(ro.OpCommon.Unrefs(), ro.UncommittedUnrefs...)
  1354  }
  1355  
  1356  func (ro *resolutionOp) DelUnrefBlock(ptr data.BlockPointer) {
  1357  	ro.OpCommon.DelUnrefBlock(ptr)
  1358  	for i, unref := range ro.UncommittedUnrefs {
  1359  		if ptr == unref {
  1360  			ro.UncommittedUnrefs = append(
  1361  				ro.UncommittedUnrefs[:i], ro.UncommittedUnrefs[i+1:]...)
  1362  			break
  1363  		}
  1364  	}
  1365  }
  1366  
  1367  func (ro *resolutionOp) SizeExceptUpdates() uint64 {
  1368  	return 0
  1369  }
  1370  
  1371  func (ro *resolutionOp) allUpdates() []blockUpdate {
  1372  	return ro.Updates
  1373  }
  1374  
  1375  func (ro *resolutionOp) checkValid() error {
  1376  	return ro.checkUpdatesValid()
  1377  }
  1378  
  1379  func (ro *resolutionOp) String() string {
  1380  	return "resolution"
  1381  }
  1382  
  1383  func (ro *resolutionOp) Plaintext() string {
  1384  	return ro.String()
  1385  }
  1386  
  1387  func (ro *resolutionOp) StringWithRefs(indent string) string {
  1388  	res := ro.String() + "\n"
  1389  	res += ro.stringWithRefs(indent)
  1390  	return res
  1391  }
  1392  
  1393  // AddUncommittedUnrefBlock adds this block to the list of blocks that should be
  1394  // archived/deleted from the server, but which were never actually
  1395  // committed successfully in an MD.  Therefore, their sizes don't have
  1396  // to be accounted for in any MD size accounting for the TLF.
  1397  func (ro *resolutionOp) AddUncommittedUnrefBlock(ptr data.BlockPointer) {
  1398  	ro.UncommittedUnrefs = append(ro.UncommittedUnrefs, ptr)
  1399  }
  1400  
  1401  func (ro *resolutionOp) CommittedUnrefs() []data.BlockPointer {
  1402  	return ro.OpCommon.Unrefs()
  1403  }
  1404  
  1405  func (ro *resolutionOp) checkConflict(
  1406  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
  1407  	isFile bool) (crAction, error) {
  1408  	return nil, nil
  1409  }
  1410  
  1411  func (ro *resolutionOp) getDefaultAction(mergedPath data.Path) crAction {
  1412  	return nil
  1413  }
  1414  
  1415  // rekeyOp is an op that represents a rekey on a TLF.
  1416  type rekeyOp struct {
  1417  	OpCommon
  1418  }
  1419  
  1420  func newRekeyOp() *rekeyOp {
  1421  	ro := &rekeyOp{}
  1422  	return ro
  1423  }
  1424  
  1425  func (ro *rekeyOp) deepCopy() op {
  1426  	roCopy := *ro
  1427  	roCopy.OpCommon = ro.OpCommon.deepCopy()
  1428  	return &roCopy
  1429  }
  1430  
  1431  func (ro *rekeyOp) SizeExceptUpdates() uint64 {
  1432  	return 0
  1433  }
  1434  
  1435  func (ro *rekeyOp) allUpdates() []blockUpdate {
  1436  	return ro.Updates
  1437  }
  1438  
  1439  func (ro *rekeyOp) checkValid() error {
  1440  	return ro.checkUpdatesValid()
  1441  }
  1442  
  1443  func (ro *rekeyOp) String() string {
  1444  	return "rekey"
  1445  }
  1446  
  1447  func (ro *rekeyOp) Plaintext() string {
  1448  	return ro.String()
  1449  }
  1450  
  1451  func (ro *rekeyOp) StringWithRefs(indent string) string {
  1452  	res := ro.String() + "\n"
  1453  	res += ro.stringWithRefs(indent)
  1454  	return res
  1455  }
  1456  
  1457  func (ro *rekeyOp) checkConflict(
  1458  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
  1459  	isFile bool) (crAction, error) {
  1460  	return nil, nil
  1461  }
  1462  
  1463  func (ro *rekeyOp) getDefaultAction(mergedPath data.Path) crAction {
  1464  	return nil
  1465  }
  1466  
  1467  // GCOp is an op that represents garbage-collecting the history of a
  1468  // folder (which may involve unreferencing blocks that previously held
  1469  // operation lists.  It may contain unref blocks before it is added to
  1470  // the metadata ops list.
  1471  type GCOp struct {
  1472  	OpCommon
  1473  
  1474  	// LatestRev is the most recent MD revision that was
  1475  	// garbage-collected with this operation.
  1476  	//
  1477  	// The codec name overrides the one for RefBlocks in OpCommon,
  1478  	// which GCOp doesn't use.
  1479  	LatestRev kbfsmd.Revision `codec:"r"`
  1480  }
  1481  
  1482  func newGCOp(latestRev kbfsmd.Revision) *GCOp {
  1483  	gco := &GCOp{
  1484  		LatestRev: latestRev,
  1485  	}
  1486  	return gco
  1487  }
  1488  
  1489  func (gco *GCOp) deepCopy() op {
  1490  	gcoCopy := *gco
  1491  	gcoCopy.OpCommon = gco.OpCommon.deepCopy()
  1492  	return &gcoCopy
  1493  }
  1494  
  1495  // SizeExceptUpdates implements op.
  1496  func (gco *GCOp) SizeExceptUpdates() uint64 {
  1497  	return data.BPSize * uint64(len(gco.UnrefBlocks))
  1498  }
  1499  
  1500  func (gco *GCOp) allUpdates() []blockUpdate {
  1501  	return gco.Updates
  1502  }
  1503  
  1504  func (gco *GCOp) checkValid() error {
  1505  	return gco.checkUpdatesValid()
  1506  }
  1507  
  1508  func (gco *GCOp) String() string {
  1509  	return fmt.Sprintf("gc %d", gco.LatestRev)
  1510  }
  1511  
  1512  // Plaintext implements op.
  1513  func (gco *GCOp) Plaintext() string {
  1514  	return gco.String()
  1515  }
  1516  
  1517  // StringWithRefs implements the op interface for GCOp.
  1518  func (gco *GCOp) StringWithRefs(indent string) string {
  1519  	res := gco.String() + "\n"
  1520  	res += gco.stringWithRefs(indent)
  1521  	return res
  1522  }
  1523  
  1524  // checkConflict implements op.
  1525  func (gco *GCOp) checkConflict(
  1526  	ctx context.Context, renamer ConflictRenamer, mergedOp op,
  1527  	isFile bool) (crAction, error) {
  1528  	return nil, nil
  1529  }
  1530  
  1531  // getDefaultAction implements op.
  1532  func (gco *GCOp) getDefaultAction(mergedPath data.Path) crAction {
  1533  	return nil
  1534  }
  1535  
  1536  // invertOpForLocalNotifications returns an operation that represents
  1537  // an undoing of the effect of the given op.  These are intended to be
  1538  // used for local notifications only, and would not be useful for
  1539  // finding conflicts (for example, we lose information about the type
  1540  // of the file in a rmOp that we are trying to re-create).
  1541  func invertOpForLocalNotifications(oldOp op) (newOp op, err error) {
  1542  	switch op := oldOp.(type) {
  1543  	default:
  1544  		panic(fmt.Sprintf("Unrecognized operation: %v", op))
  1545  	case *createOp:
  1546  		newOp, err = newRmOp(op.NewName, op.Dir.Ref, op.Type)
  1547  		if err != nil {
  1548  			return nil, err
  1549  		}
  1550  	case *rmOp:
  1551  		// Guess at the type, shouldn't be used for local notification
  1552  		// purposes.
  1553  		newOp, err = newCreateOp(op.OldName, op.Dir.Ref, data.File)
  1554  		if err != nil {
  1555  			return nil, err
  1556  		}
  1557  	case *renameOp:
  1558  		newDirRef := op.NewDir.Ref
  1559  		if op.NewDir == (blockUpdate{}) {
  1560  			newDirRef = op.OldDir.Ref
  1561  		}
  1562  		newOp, err = newRenameOp(op.NewName, newDirRef,
  1563  			op.OldName, op.OldDir.Ref, op.Renamed, op.RenamedType)
  1564  		if err != nil {
  1565  			return nil, err
  1566  		}
  1567  	case *syncOp:
  1568  		// Just replay the writes; for notifications purposes, they
  1569  		// will do the right job of marking the right bytes as
  1570  		// invalid.
  1571  		so, err := newSyncOp(op.File.Ref)
  1572  		if err != nil {
  1573  			return nil, err
  1574  		}
  1575  		so.Writes = make([]WriteRange, len(op.Writes))
  1576  		copy(so.Writes, op.Writes)
  1577  		newOp = so
  1578  	case *setAttrOp:
  1579  		newOp, err = newSetAttrOp(op.Name, op.Dir.Ref, op.Attr, op.File)
  1580  		if err != nil {
  1581  			return nil, err
  1582  		}
  1583  	case *GCOp:
  1584  		newOp = newGCOp(op.LatestRev)
  1585  	case *resolutionOp:
  1586  		newOp = newResolutionOp()
  1587  	case *rekeyOp:
  1588  		newOp = newRekeyOp()
  1589  	}
  1590  	newOp.setFinalPath(oldOp.getFinalPath())
  1591  	// Now reverse all the block updates.  Don't bother with bare Refs
  1592  	// and Unrefs since they don't matter for local notification
  1593  	// purposes.
  1594  	for _, update := range oldOp.allUpdates() {
  1595  		newOp.AddUpdate(update.Ref, update.Unref)
  1596  	}
  1597  	return newOp, nil
  1598  }
  1599  
  1600  // NOTE: If you're updating opPointerizer and RegisterOps, make sure
  1601  // to also update opPointerizerFuture and registerOpsFuture in
  1602  // ops_test.go.
  1603  
  1604  // Our ugorji codec cannot decode our extension types as pointers, and
  1605  // we need them to be pointers so they correctly satisfy the op
  1606  // interface.  So this function simply converts them into pointers as
  1607  // needed.
  1608  func opPointerizer(iface interface{}) reflect.Value {
  1609  	switch op := iface.(type) {
  1610  	default:
  1611  		return reflect.ValueOf(iface)
  1612  	case createOp:
  1613  		return reflect.ValueOf(&op)
  1614  	case rmOp:
  1615  		return reflect.ValueOf(&op)
  1616  	case renameOp:
  1617  		return reflect.ValueOf(&op)
  1618  	case syncOp:
  1619  		return reflect.ValueOf(&op)
  1620  	case setAttrOp:
  1621  		return reflect.ValueOf(&op)
  1622  	case resolutionOp:
  1623  		return reflect.ValueOf(&op)
  1624  	case rekeyOp:
  1625  		return reflect.ValueOf(&op)
  1626  	case GCOp:
  1627  		return reflect.ValueOf(&op)
  1628  	}
  1629  }
  1630  
  1631  // RegisterOps registers all op types with the given codec.
  1632  func RegisterOps(codec kbfscodec.Codec) {
  1633  	codec.RegisterType(reflect.TypeOf(createOp{}), createOpCode)
  1634  	codec.RegisterType(reflect.TypeOf(rmOp{}), rmOpCode)
  1635  	codec.RegisterType(reflect.TypeOf(renameOp{}), renameOpCode)
  1636  	codec.RegisterType(reflect.TypeOf(syncOp{}), syncOpCode)
  1637  	codec.RegisterType(reflect.TypeOf(setAttrOp{}), setAttrOpCode)
  1638  	codec.RegisterType(reflect.TypeOf(resolutionOp{}), resolutionOpCode)
  1639  	codec.RegisterType(reflect.TypeOf(rekeyOp{}), rekeyOpCode)
  1640  	codec.RegisterType(reflect.TypeOf(GCOp{}), gcOpCode)
  1641  	codec.RegisterIfaceSliceType(reflect.TypeOf(opsList{}), opsListCode,
  1642  		opPointerizer)
  1643  }
  1644  
  1645  // pathSortedOps sorts the ops in increasing order by path length, so
  1646  // e.g. file creates come before file modifies.
  1647  type pathSortedOps []op
  1648  
  1649  func (pso pathSortedOps) Len() int {
  1650  	return len(pso)
  1651  }
  1652  
  1653  func (pso pathSortedOps) Less(i, j int) bool {
  1654  	return len(pso[i].getFinalPath().Path) < len(pso[j].getFinalPath().Path)
  1655  }
  1656  
  1657  func (pso pathSortedOps) Swap(i, j int) {
  1658  	pso[i], pso[j] = pso[j], pso[i]
  1659  }