github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/data/file_data.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 data
     6  
     7  import (
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/keybase/client/go/kbfs/kbfsblock"
    12  	"github.com/keybase/client/go/kbfs/libkey"
    13  	"github.com/keybase/client/go/kbfs/tlf"
    14  	"github.com/keybase/client/go/libkb"
    15  	"github.com/keybase/client/go/logger"
    16  	"github.com/keybase/client/go/protocol/keybase1"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  // FileBlockGetter is a function that gets a block suitable for
    21  // reading or writing, and also returns whether the block was already
    22  // dirty.  It may be called from new goroutines, and must handle any
    23  // required locks accordingly.
    24  type FileBlockGetter func(context.Context, libkey.KeyMetadata, BlockPointer,
    25  	Path, BlockReqType) (fblock *FileBlock, wasDirty bool, err error)
    26  
    27  // FileData is a helper struct for accessing and manipulating data
    28  // within a file.  It's meant for use within a single scope, not for
    29  // long-term storage.  The caller must ensure goroutine-safety.
    30  type FileData struct {
    31  	getter FileBlockGetter
    32  	tree   *blockTree
    33  }
    34  
    35  // NewFileData makes a new file data object for the given `file`
    36  // within the given `kmd`.
    37  func NewFileData(
    38  	file Path, chargedTo keybase1.UserOrTeamID, bsplit BlockSplitter,
    39  	kmd libkey.KeyMetadata, getter FileBlockGetter,
    40  	cacher dirtyBlockCacher, log logger.Logger,
    41  	vlog *libkb.VDebugLog) *FileData {
    42  	fd := &FileData{
    43  		getter: getter,
    44  	}
    45  	fd.tree = &blockTree{
    46  		file:      file,
    47  		chargedTo: chargedTo,
    48  		kmd:       kmd,
    49  		bsplit:    bsplit,
    50  		getter:    fd.blockGetter,
    51  		cacher:    cacher,
    52  		log:       log,
    53  		vlog:      vlog,
    54  	}
    55  	return fd
    56  }
    57  
    58  func (fd *FileData) rootBlockPointer() BlockPointer {
    59  	return fd.tree.file.TailPointer()
    60  }
    61  
    62  func (fd *FileData) blockGetter(
    63  	ctx context.Context, kmd libkey.KeyMetadata, ptr BlockPointer,
    64  	file Path, rtype BlockReqType) (
    65  	block BlockWithPtrs, wasDirty bool, err error) {
    66  	return fd.getter(ctx, kmd, ptr, file, rtype)
    67  }
    68  
    69  func (fd *FileData) getLeafBlocksForOffsetRange(ctx context.Context,
    70  	ptr BlockPointer, pblock *FileBlock, startOff, endOff Int64Offset,
    71  	prefixOk bool) (pathsFromRoot [][]ParentBlockAndChildIndex,
    72  	blocks map[BlockPointer]Block, nextBlockOffset Int64Offset,
    73  	err error) {
    74  	var eo Offset
    75  	if endOff >= 0 {
    76  		eo = endOff
    77  	}
    78  	pathsFromRoot, blocks, nbo, err := fd.tree.getBlocksForOffsetRange(
    79  		ctx, ptr, pblock, startOff, eo, prefixOk, true)
    80  	if err != nil {
    81  		return nil, nil, 0, err
    82  	}
    83  	if nbo != nil {
    84  		nextBlockOffset = nbo.(Int64Offset)
    85  	} else {
    86  		nextBlockOffset = -1
    87  	}
    88  	return pathsFromRoot, blocks, nextBlockOffset, nil
    89  }
    90  
    91  func childFileIptr(p ParentBlockAndChildIndex) IndirectFilePtr {
    92  	fb := p.pblock.(*FileBlock)
    93  	return fb.IPtrs[p.childIndex]
    94  }
    95  
    96  // getByteSlicesInOffsetRange returns an ordered, continuous slice of
    97  // byte ranges for the data described by the half-inclusive offset
    98  // range `[startOff, endOff)`.  If `endOff` == -1, it returns data to
    99  // the end of the file.  The caller is responsible for concatenating
   100  // the data into a single buffer if desired. If `prefixOk` is true,
   101  // the function will ignore context deadline errors and return
   102  // whatever prefix of the data it could fetch within the deadine.
   103  func (fd *FileData) getByteSlicesInOffsetRange(ctx context.Context,
   104  	startOff, endOff Int64Offset, prefixOk bool) ([][]byte, error) {
   105  	if startOff < 0 || endOff < -1 {
   106  		return nil, fmt.Errorf("Bad offset range [%d, %d)", startOff, endOff)
   107  	} else if endOff != -1 && endOff <= startOff {
   108  		return nil, nil
   109  	}
   110  
   111  	topBlock, _, err := fd.getter(ctx, fd.tree.kmd, fd.rootBlockPointer(),
   112  		fd.tree.file, BlockRead)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	// Find all the indirect pointers to leaf blocks in the offset range.
   118  	var iptrs []IndirectFilePtr
   119  	endBlockOff := Int64Offset(-1)
   120  	nextBlockOff := Int64Offset(-1)
   121  	var blockMap map[BlockPointer]Block
   122  	if topBlock.IsInd {
   123  		var pfr [][]ParentBlockAndChildIndex
   124  		pfr, blockMap, nextBlockOff, err = fd.getLeafBlocksForOffsetRange(
   125  			ctx, fd.rootBlockPointer(), topBlock, startOff, endOff, prefixOk)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  
   130  		for i, p := range pfr {
   131  			if len(p) == 0 {
   132  				return nil, fmt.Errorf("Unexpected empty path to child for "+
   133  					"file %v", fd.rootBlockPointer())
   134  			}
   135  			lowestAncestor := p[len(p)-1]
   136  			iptr := childFileIptr(lowestAncestor)
   137  			iptrs = append(iptrs, iptr)
   138  			if i == len(pfr)-1 {
   139  				leafBlock := blockMap[iptr.BlockPointer].(*FileBlock)
   140  				endBlockOff = iptr.Off + Int64Offset(len(leafBlock.Contents))
   141  			}
   142  		}
   143  	} else {
   144  		iptrs = []IndirectFilePtr{{
   145  			BlockInfo: BlockInfo{BlockPointer: fd.rootBlockPointer()},
   146  			Off:       0,
   147  		}}
   148  		endBlockOff = Int64Offset(len(topBlock.Contents))
   149  		blockMap = map[BlockPointer]Block{fd.rootBlockPointer(): topBlock}
   150  	}
   151  
   152  	if len(iptrs) == 0 {
   153  		return nil, nil
   154  	}
   155  
   156  	nRead := int64(0)
   157  	n := int64(endOff - startOff)
   158  	if endOff == -1 {
   159  		n = int64(endBlockOff - startOff)
   160  	}
   161  
   162  	// Grab the relevant byte slices from each block described by the
   163  	// indirect pointer, filling in holes as needed.
   164  	var bytes [][]byte
   165  	for i, iptr := range iptrs {
   166  		block := blockMap[iptr.BlockPointer].(*FileBlock)
   167  		blockLen := int64(len(block.Contents))
   168  		nextByte := nRead + int64(startOff)
   169  		toRead := n - nRead
   170  		blockOff := iptr.Off
   171  		lastByteInBlock := int64(blockOff) + blockLen
   172  
   173  		nextIPtrOff := nextBlockOff
   174  		if i < len(iptrs)-1 {
   175  			nextIPtrOff = iptrs[i+1].Off
   176  		}
   177  
   178  		if nextByte >= lastByteInBlock {
   179  			if nextIPtrOff > 0 {
   180  				fill := int64(nextIPtrOff) - nextByte
   181  				if fill > toRead {
   182  					fill = toRead
   183  				}
   184  				fd.tree.vlog.CLogf(
   185  					ctx, libkb.VLog1, "Read from hole: nextByte=%d "+
   186  						"lastByteInBlock=%d fill=%d", nextByte, lastByteInBlock,
   187  					fill)
   188  				if fill <= 0 {
   189  					fd.tree.log.CErrorf(ctx,
   190  						"Read invalid file fill <= 0 while reading hole")
   191  					return nil, BadSplitError{}
   192  				}
   193  				bytes = append(bytes, make([]byte, fill))
   194  				nRead += fill
   195  				continue
   196  			}
   197  			return bytes, nil
   198  		} else if toRead > lastByteInBlock-nextByte {
   199  			toRead = lastByteInBlock - nextByte
   200  		}
   201  
   202  		// Check for holes in the middle of a file.
   203  		if nextByte < int64(blockOff) {
   204  			fill := int64(blockOff) - nextByte
   205  			bytes = append(bytes, make([]byte, fill))
   206  			nRead += fill
   207  			nextByte += fill
   208  			toRead -= fill
   209  		}
   210  
   211  		firstByteToRead := nextByte - int64(blockOff)
   212  		bytes = append(bytes,
   213  			block.Contents[firstByteToRead:toRead+firstByteToRead])
   214  		nRead += toRead
   215  	}
   216  
   217  	// If we didn't complete the read and there's another block, then
   218  	// we've hit another hole and need to add a fill.
   219  	if nRead < n && nextBlockOff > 0 {
   220  		toRead := n - nRead
   221  		nextByte := nRead + int64(startOff)
   222  		fill := int64(nextBlockOff) - nextByte
   223  		if fill > toRead {
   224  			fill = toRead
   225  		}
   226  		fd.tree.vlog.CLogf(
   227  			ctx, libkb.VLog1, "Read from hole at end of file: nextByte=%d "+
   228  				"fill=%d", nextByte, fill)
   229  		if fill <= 0 {
   230  			fd.tree.log.CErrorf(ctx,
   231  				"Read invalid file fill <= 0 while reading hole")
   232  			return nil, BadSplitError{}
   233  		}
   234  		bytes = append(bytes, make([]byte, fill))
   235  	}
   236  	return bytes, nil
   237  }
   238  
   239  // The amount that the read timeout is smaller than the global one.
   240  const readTimeoutSmallerBy = 2 * time.Second
   241  
   242  // read fills the `dest` buffer with data from the file, starting at
   243  // `startOff`.  Returns the number of bytes copied.  If the read
   244  // operation nears the deadline set in `ctx`, it returns as big a
   245  // prefix as possible before reaching the deadline.
   246  func (fd *FileData) Read(ctx context.Context, dest []byte,
   247  	startOff Int64Offset) (int64, error) {
   248  	if len(dest) == 0 {
   249  		return 0, nil
   250  	}
   251  
   252  	// If we have a large enough timeout add a temporary timeout that is
   253  	// readTimeoutSmallerBy. Use that for reading so short reads get returned
   254  	// upstream without triggering the global timeout.
   255  	now := time.Now()
   256  	deadline, haveTimeout := ctx.Deadline()
   257  	if haveTimeout {
   258  		rem := deadline.Sub(now) - readTimeoutSmallerBy
   259  		if rem > 0 {
   260  			var cancel func()
   261  			ctx, cancel = context.WithTimeout(ctx, rem)
   262  			defer cancel()
   263  		}
   264  	}
   265  
   266  	bytes, err := fd.getByteSlicesInOffsetRange(ctx, startOff,
   267  		startOff+Int64Offset(len(dest)), true)
   268  	if err != nil {
   269  		return 0, err
   270  	}
   271  
   272  	currLen := int64(0)
   273  	for _, b := range bytes {
   274  		bLen := int64(len(b))
   275  		copy(dest[currLen:currLen+bLen], b)
   276  		currLen += bLen
   277  	}
   278  	return currLen, nil
   279  }
   280  
   281  // GetBytes returns a buffer containing data from the file, in the
   282  // half-inclusive range `[startOff, endOff)`.  If `endOff` == -1, it
   283  // returns data until the end of the file.
   284  func (fd *FileData) GetBytes(ctx context.Context,
   285  	startOff, endOff Int64Offset) (data []byte, err error) {
   286  	bytes, err := fd.getByteSlicesInOffsetRange(ctx, startOff, endOff, false)
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  
   291  	bufSize := 0
   292  	for _, b := range bytes {
   293  		bufSize += len(b)
   294  	}
   295  	data = make([]byte, bufSize)
   296  	currLen := 0
   297  	for _, b := range bytes {
   298  		copy(data[currLen:currLen+len(b)], b)
   299  		currLen += len(b)
   300  	}
   301  
   302  	return data, nil
   303  }
   304  
   305  // createIndirectBlock creates a new indirect block and pick a new id
   306  // for the existing block, and use the existing block's ID for the new
   307  // indirect block that becomes the parent.
   308  func (fd *FileData) createIndirectBlock(
   309  	ctx context.Context, df *DirtyFile, dver Ver) (*FileBlock, error) {
   310  	newID, err := kbfsblock.MakeTemporaryID()
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	fblock := &FileBlock{
   315  		CommonBlock: CommonBlock{
   316  			IsInd: true,
   317  		},
   318  		IPtrs: []IndirectFilePtr{
   319  			{
   320  				BlockInfo: BlockInfo{
   321  					BlockPointer: BlockPointer{
   322  						ID:      newID,
   323  						KeyGen:  fd.tree.kmd.LatestKeyGeneration(),
   324  						DataVer: dver,
   325  						Context: kbfsblock.MakeFirstContext(
   326  							fd.tree.chargedTo,
   327  							fd.rootBlockPointer().GetBlockType()),
   328  						DirectType: fd.rootBlockPointer().DirectType,
   329  					},
   330  					EncodedSize: 0,
   331  				},
   332  				Off: 0,
   333  			},
   334  		},
   335  	}
   336  
   337  	fd.tree.vlog.CLogf(
   338  		ctx, libkb.VLog1, "Creating new level of indirection for file %v, "+
   339  			"new block id for old top level is %v", fd.rootBlockPointer(), newID)
   340  
   341  	// Mark the old block ID as not dirty, so that we will treat the
   342  	// old block ID as newly dirtied in cacheBlockIfNotYetDirtyLocked.
   343  	df.setBlockNotDirty(fd.rootBlockPointer())
   344  	err = fd.tree.cacher(ctx, fd.rootBlockPointer(), fblock)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	return fblock, nil
   350  }
   351  
   352  // GetFileBlockAtOffset returns the leaf file block responsible for
   353  // the given offset.
   354  func (fd *FileData) GetFileBlockAtOffset(ctx context.Context,
   355  	topBlock *FileBlock, off Int64Offset, rtype BlockReqType) (
   356  	ptr BlockPointer, parentBlocks []ParentBlockAndChildIndex,
   357  	block *FileBlock, nextBlockStartOff, startOff Int64Offset,
   358  	wasDirty bool, err error) {
   359  	ptr, parentBlocks, b, nbso, so, wasDirty, err := fd.tree.getBlockAtOffset(
   360  		ctx, topBlock, off, rtype)
   361  	if err != nil {
   362  		return ZeroPtr, nil, nil, 0, 0, false, err
   363  	}
   364  	if b != nil {
   365  		block = b.(*FileBlock)
   366  	}
   367  	if nbso != nil {
   368  		nextBlockStartOff = nbso.(Int64Offset)
   369  	} else {
   370  		nextBlockStartOff = -1
   371  	}
   372  	if so != nil {
   373  		startOff = so.(Int64Offset)
   374  	}
   375  	return ptr, parentBlocks, block, nextBlockStartOff, startOff, wasDirty, nil
   376  }
   377  
   378  func (fd *FileData) fileTopBlocker(df *DirtyFile) createTopBlockFn {
   379  	return func(ctx context.Context, dv Ver) (BlockWithPtrs, error) {
   380  		return fd.createIndirectBlock(ctx, df, dv)
   381  	}
   382  }
   383  
   384  // Write sets the given data and the given offset within the file,
   385  // making new blocks and new levels of indirection as needed. Return
   386  // params:
   387  //   - newDe: a new directory entry with the EncodedSize cleared if the file
   388  //     was extended.
   389  //   - dirtyPtrs: a slice of the BlockPointers that have been dirtied during
   390  //     the write.  This includes any interior indirect blocks that may not
   391  //     have been changed yet, but which will need to change as part of the
   392  //     sync process because of leaf node changes below it.
   393  //   - unrefs: a slice of BlockInfos that must be unreferenced as part of an
   394  //     eventual sync of this write.  May be non-nil even if err != nil.
   395  //   - newlyDirtiedChildBytes is the total amount of block data dirtied by this
   396  //     write, including the entire size of blocks that have had at least one
   397  //     byte dirtied.  As above, it may be non-zero even if err != nil.
   398  //   - bytesExtended is the number of bytes the length of the file has been
   399  //     extended as part of this write.
   400  func (fd *FileData) Write(ctx context.Context, data []byte, off Int64Offset,
   401  	topBlock *FileBlock, oldDe DirEntry, df *DirtyFile) (
   402  	newDe DirEntry, dirtyPtrs []BlockPointer, unrefs []BlockInfo,
   403  	newlyDirtiedChildBytes int64, bytesExtended int64, err error) {
   404  	n := int64(len(data))
   405  	nCopied := int64(0)
   406  	oldSizeWithoutHoles := oldDe.Size
   407  	newDe = oldDe
   408  
   409  	fd.tree.vlog.CLogf(ctx, libkb.VLog1, "Writing %d bytes at off %d", n, off)
   410  
   411  	dirtyMap := make(map[BlockPointer]bool)
   412  	for nCopied < n {
   413  		ptr, parentBlocks, block, nextBlockOff, startOff, wasDirty, err :=
   414  			fd.GetFileBlockAtOffset(
   415  				ctx, topBlock, off+Int64Offset(nCopied), BlockWrite)
   416  		if err != nil {
   417  			return newDe, nil, unrefs, newlyDirtiedChildBytes, 0, err
   418  		}
   419  
   420  		oldLen := len(block.Contents)
   421  
   422  		// Take care not to write past the beginning of the next block
   423  		// by using max.
   424  		max := Int64Offset(len(data))
   425  		if nextBlockOff > 0 {
   426  			if room := nextBlockOff - off; room < max {
   427  				max = room
   428  			}
   429  		}
   430  		oldNCopied := nCopied
   431  		nCopied += fd.tree.bsplit.CopyUntilSplit(
   432  			block, nextBlockOff < 0, data[nCopied:max],
   433  			int64(off+Int64Offset(nCopied)-startOff))
   434  
   435  		// If we need another block but there are no more, then make one.
   436  		switchToIndirect := false
   437  		if nCopied < n {
   438  			needExtendFile := nextBlockOff < 0
   439  			needFillHole := off+Int64Offset(nCopied) < nextBlockOff
   440  			newBlockOff := startOff + Int64Offset(len(block.Contents))
   441  			if nCopied == 0 {
   442  				if newBlockOff < off {
   443  					// We are writing past the end of a file, or
   444  					// somewhere inside a hole, not right at the start
   445  					// of it; all we have done so far it reached the
   446  					// end of an existing block (possibly zero-filling
   447  					// it out to its capacity).  Make sure the next
   448  					// block starts right at the offset we care about.
   449  					newBlockOff = off
   450  				}
   451  			} else if newBlockOff != off+Int64Offset(nCopied) {
   452  				return newDe, nil, unrefs, newlyDirtiedChildBytes, 0,
   453  					fmt.Errorf("Copied %d bytes, but newBlockOff=%d does not "+
   454  						"match off=%d plus new bytes",
   455  						nCopied, newBlockOff, off)
   456  			}
   457  			var rightParents []ParentBlockAndChildIndex
   458  			if needExtendFile || needFillHole {
   459  				// Make a new right block and update the parent's
   460  				// indirect block list, adding a level of indirection
   461  				// if needed.  If we're just filling a hole, the block
   462  				// will end up all the way to the right of the range,
   463  				// and its offset will be smaller than the block to
   464  				// its left -- we'll fix that up below.
   465  				var newDirtyPtrs []BlockPointer
   466  				fd.tree.vlog.CLogf(
   467  					ctx, libkb.VLog1, "Making new right block at "+
   468  						"nCopied=%d, newBlockOff=%d", nCopied, newBlockOff)
   469  				wasIndirect := topBlock.IsInd
   470  				rightParents, newDirtyPtrs, err = fd.tree.newRightBlock(
   471  					ctx, parentBlocks, newBlockOff,
   472  					DefaultNewBlockDataVersion(false), NewFileBlockWithPtrs,
   473  					fd.fileTopBlocker(df),
   474  				)
   475  				if err != nil {
   476  					return newDe, nil, unrefs, newlyDirtiedChildBytes, 0, err
   477  				}
   478  				topBlock = rightParents[0].pblock.(*FileBlock)
   479  				for _, p := range newDirtyPtrs {
   480  					dirtyMap[p] = true
   481  				}
   482  				if topBlock.IsInd != wasIndirect {
   483  					// The whole direct data block needs to be
   484  					// re-uploaded as a child block with a new block
   485  					// pointer, so below we'll need to track the dirty
   486  					// bytes of the direct block and cache the block
   487  					// as dirty.  (Note that currently we don't track
   488  					// dirty bytes for indirect blocks.)
   489  					switchToIndirect = true
   490  					ptr = topBlock.IPtrs[0].BlockPointer
   491  				}
   492  			}
   493  			// If we're filling a hole, swap the new right block into
   494  			// the hole and shift everything else over.
   495  			if needFillHole {
   496  				newDirtyPtrs, newUnrefs, bytes, err :=
   497  					fd.tree.shiftBlocksToFillHole(ctx, rightParents)
   498  				if err != nil {
   499  					return newDe, nil, unrefs, newlyDirtiedChildBytes, 0, err
   500  				}
   501  				for _, p := range newDirtyPtrs {
   502  					dirtyMap[p] = true
   503  				}
   504  				unrefs = append(unrefs, newUnrefs...)
   505  				newlyDirtiedChildBytes += bytes
   506  				if oldSizeWithoutHoles == oldDe.Size {
   507  					// For the purposes of calculating the newly-dirtied
   508  					// bytes for the deferral calculation, disregard the
   509  					// existing "hole" in the file.
   510  					oldSizeWithoutHoles = uint64(newBlockOff)
   511  				}
   512  			}
   513  		}
   514  
   515  		// Nothing was copied, no need to dirty anything.  This can
   516  		// happen when trying to append to the contents of the file
   517  		// (i.e., either to the end of the file or right before the
   518  		// "hole"), and the last block is already full.
   519  		if nCopied == oldNCopied && oldLen == len(block.Contents) &&
   520  			!switchToIndirect {
   521  			continue
   522  		}
   523  
   524  		// Only in the last block does the file size grow.
   525  		if oldLen != len(block.Contents) && nextBlockOff < 0 {
   526  			newDe.EncodedSize = 0
   527  			// Since this is the last block, the end of this block
   528  			// marks the file size.
   529  			newDe.Size = uint64(startOff + Int64Offset(len(block.Contents)))
   530  		}
   531  
   532  		// Calculate the amount of bytes we've newly-dirtied as part
   533  		// of this write.
   534  		newlyDirtiedChildBytes += int64(len(block.Contents))
   535  		if wasDirty {
   536  			newlyDirtiedChildBytes -= int64(oldLen)
   537  		}
   538  
   539  		newDirtyPtrs, newUnrefs, err := fd.tree.markParentsDirty(
   540  			ctx, parentBlocks)
   541  		unrefs = append(unrefs, newUnrefs...)
   542  		if err != nil {
   543  			return newDe, nil, unrefs, newlyDirtiedChildBytes, 0, err
   544  		}
   545  		for _, p := range newDirtyPtrs {
   546  			dirtyMap[p] = true
   547  		}
   548  
   549  		// keep the old block ID while it's dirty
   550  		if err = fd.tree.cacher(ctx, ptr, block); err != nil {
   551  			return newDe, nil, unrefs, newlyDirtiedChildBytes, 0, err
   552  		}
   553  		dirtyMap[ptr] = true
   554  	}
   555  
   556  	// Always make the top block dirty, so we will sync any indirect
   557  	// blocks.  This has the added benefit of ensuring that any write
   558  	// to a file while it's being sync'd will be deferred, even if
   559  	// it's to a block that's not currently being sync'd, since this
   560  	// top-most block will always be in the dirtyFiles map.  We do
   561  	// this even for 0-byte writes, which indicate a forced sync.
   562  	if err = fd.tree.cacher(ctx, fd.rootBlockPointer(), topBlock); err != nil {
   563  		return newDe, nil, unrefs, newlyDirtiedChildBytes, 0, err
   564  	}
   565  	dirtyMap[fd.rootBlockPointer()] = true
   566  
   567  	lastByteWritten := int64(off) + int64(len(data)) // not counting holes
   568  	bytesExtended = 0
   569  	if lastByteWritten > int64(oldSizeWithoutHoles) {
   570  		bytesExtended = lastByteWritten - int64(oldSizeWithoutHoles)
   571  	}
   572  
   573  	dirtyPtrs = make([]BlockPointer, 0, len(dirtyMap))
   574  	for p := range dirtyMap {
   575  		dirtyPtrs = append(dirtyPtrs, p)
   576  	}
   577  
   578  	return newDe, dirtyPtrs, unrefs, newlyDirtiedChildBytes, bytesExtended, nil
   579  }
   580  
   581  // TruncateExtend increases file size to the given size by appending
   582  // a "hole" to the file. Return params:
   583  //   - newDe: a new directory entry with the EncodedSize cleared.
   584  //   - dirtyPtrs: a slice of the BlockPointers that have been dirtied during
   585  //     the truncate.
   586  func (fd *FileData) TruncateExtend(ctx context.Context, size uint64,
   587  	topBlock *FileBlock, parentBlocks []ParentBlockAndChildIndex,
   588  	oldDe DirEntry, df *DirtyFile) (
   589  	newDe DirEntry, dirtyPtrs []BlockPointer, err error) {
   590  	fd.tree.vlog.CLogf(
   591  		ctx, libkb.VLog1, "truncateExtend: extending file %v to size %d",
   592  		fd.rootBlockPointer(), size)
   593  	switchToIndirect := !topBlock.IsInd
   594  	oldTopBlock := topBlock
   595  	if switchToIndirect {
   596  		fd.tree.vlog.CLogf(
   597  			ctx, libkb.VLog1, "truncateExtend: making block indirect %v",
   598  			fd.rootBlockPointer())
   599  	}
   600  
   601  	rightParents, newDirtyPtrs, err := fd.tree.newRightBlock(
   602  		ctx, parentBlocks, Int64Offset(size),
   603  		DefaultNewBlockDataVersion(true), NewFileBlockWithPtrs,
   604  		fd.fileTopBlocker(df))
   605  	if err != nil {
   606  		return DirEntry{}, nil, err
   607  	}
   608  	topBlock = rightParents[0].pblock.(*FileBlock)
   609  
   610  	if switchToIndirect {
   611  		topBlock.IPtrs[0].Holes = true
   612  		err = fd.tree.cacher(ctx, topBlock.IPtrs[0].BlockPointer, oldTopBlock)
   613  		if err != nil {
   614  			return DirEntry{}, nil, err
   615  		}
   616  		dirtyPtrs = append(dirtyPtrs, topBlock.IPtrs[0].BlockPointer)
   617  		fd.tree.vlog.CLogf(
   618  			ctx, libkb.VLog1, "truncateExtend: new zero data block %v",
   619  			topBlock.IPtrs[0].BlockPointer)
   620  	}
   621  	dirtyPtrs = append(dirtyPtrs, newDirtyPtrs...)
   622  	newDe = oldDe
   623  	newDe.EncodedSize = 0
   624  	// update the file info
   625  	newDe.Size = size
   626  
   627  	// Mark all for presence of holes, one would be enough,
   628  	// but this is more robust and easy.
   629  	for i := range topBlock.IPtrs {
   630  		topBlock.IPtrs[i].Holes = true
   631  	}
   632  	// Always make the top block dirty, so we will sync its
   633  	// indirect blocks.  This has the added benefit of ensuring
   634  	// that any write to a file while it's being sync'd will be
   635  	// deferred, even if it's to a block that's not currently
   636  	// being sync'd, since this top-most block will always be in
   637  	// the fileBlockStates map.
   638  	err = fd.tree.cacher(ctx, fd.rootBlockPointer(), topBlock)
   639  	if err != nil {
   640  		return DirEntry{}, nil, err
   641  	}
   642  	dirtyPtrs = append(dirtyPtrs, fd.rootBlockPointer())
   643  	return newDe, dirtyPtrs, nil
   644  }
   645  
   646  // TruncateShrink shrinks the file to the given size. Return params:
   647  //   - newDe: a new directory entry with the EncodedSize cleared if the file
   648  //     shrunk.
   649  //   - dirtyPtrs: a slice of the BlockPointers that have been dirtied during
   650  //     the truncate.  This includes any interior indirect blocks that may not
   651  //     have been changed yet, but which will need to change as part of the
   652  //     sync process because of leaf node changes below it.
   653  //   - unrefs: a slice of BlockInfos that must be unreferenced as part of an
   654  //     eventual sync of this write.  May be non-nil even if err != nil.
   655  //   - newlyDirtiedChildBytes is the total amount of block data dirtied by this
   656  //     truncate, including the entire size of blocks that have had at least one
   657  //     byte dirtied.  As above, it may be non-zero even if err != nil.
   658  func (fd *FileData) TruncateShrink(ctx context.Context, size uint64,
   659  	topBlock *FileBlock, oldDe DirEntry) (
   660  	newDe DirEntry, dirtyPtrs []BlockPointer, unrefs []BlockInfo,
   661  	newlyDirtiedChildBytes int64, err error) {
   662  	iSize := Int64Offset(size) // TODO: deal with overflow
   663  
   664  	ptr, parentBlocks, block, nextBlockOff, startOff, wasDirty, err :=
   665  		fd.GetFileBlockAtOffset(ctx, topBlock, iSize, BlockWrite)
   666  	if err != nil {
   667  		return DirEntry{}, nil, nil, 0, err
   668  	}
   669  
   670  	oldLen := len(block.Contents)
   671  	// We need to delete some data (and possibly entire blocks).  Note
   672  	// we make a new slice and copy data in order to make sure the
   673  	// data being truncated can be fully garbage-collected.
   674  	block.Contents = append([]byte(nil), block.Contents[:iSize-startOff]...)
   675  
   676  	newlyDirtiedChildBytes = int64(len(block.Contents))
   677  	if wasDirty {
   678  		newlyDirtiedChildBytes -= int64(oldLen) // negative
   679  	}
   680  
   681  	// Need to mark the parents dirty before calling
   682  	// `getIndirectBlocksForOffsetRange`, so that function will see
   683  	// the new copies when fetching the blocks.
   684  	newDirtyPtrs, newUnrefs, err := fd.tree.markParentsDirty(ctx, parentBlocks)
   685  	unrefs = append(unrefs, newUnrefs...)
   686  	if err != nil {
   687  		return DirEntry{}, nil, unrefs, newlyDirtiedChildBytes, err
   688  	}
   689  	dirtyMap := make(map[BlockPointer]bool)
   690  	for _, p := range newDirtyPtrs {
   691  		dirtyMap[p] = true
   692  	}
   693  
   694  	if nextBlockOff > 0 {
   695  		// TODO: remove any unnecessary levels of indirection if the
   696  		// number of leaf nodes shrinks significantly (KBFS-1824).
   697  
   698  		// Get all paths to any leaf nodes following the new
   699  		// right-most block, since those blocks need to be
   700  		// unreferenced, and their parents need to be modified or
   701  		// unreferenced.
   702  		pfr, err := fd.tree.getIndirectBlocksForOffsetRange(
   703  			ctx, topBlock, nextBlockOff, nil)
   704  		if err != nil {
   705  			return DirEntry{}, nil, nil, 0, err
   706  		}
   707  
   708  		// A map from a pointer to an indirect block -> that block's
   709  		// original set of block pointers, before they are truncated
   710  		// in the loop below.  It also tracks which pointed-to blocks
   711  		// have already been processed.
   712  		savedChildPtrs := make(map[BlockPointer][]IndirectFilePtr)
   713  		for _, Path := range pfr {
   714  			// parentInfo points to pb.pblock in the loop below.  The
   715  			// initial case is the top block, for which we need to
   716  			// fake a block info using just the root block pointer.
   717  			parentInfo := BlockInfo{BlockPointer: fd.rootBlockPointer()}
   718  			leftMost := true
   719  			for i, pb := range Path {
   720  				ptrs := savedChildPtrs[parentInfo.BlockPointer]
   721  				if ptrs == nil {
   722  					// Process each block exactly once, removing all
   723  					// now-unnecessary indirect pointers (but caching
   724  					// that list so we can still walk the tree on the
   725  					// next iterations).
   726  					pblock := pb.pblock.(*FileBlock)
   727  					ptrs = pblock.IPtrs
   728  					savedChildPtrs[parentInfo.BlockPointer] = ptrs
   729  
   730  					// Remove the first child iptr and everything
   731  					// following it if all the child indices below
   732  					// this level are 0.
   733  					removeStartingFromIndex := pb.childIndex
   734  					for j := i + 1; j < len(Path); j++ {
   735  						if Path[j].childIndex > 0 {
   736  							removeStartingFromIndex++
   737  							break
   738  						}
   739  					}
   740  
   741  					// If we remove iptr 0, this block can be
   742  					// unreferenced (unless it's on the left-most edge
   743  					// of the tree, in which case we keep it around
   744  					// for now -- see above TODO).
   745  					if pb.childIndex == 0 && !leftMost {
   746  						if parentInfo.EncodedSize != 0 {
   747  							unrefs = append(unrefs, parentInfo)
   748  						}
   749  					} else if removeStartingFromIndex < len(pblock.IPtrs) {
   750  						// Make sure we're modifying a copy of the
   751  						// block by fetching it again with blockWrite.
   752  						// We do this instead of calling DeepCopy in
   753  						// case the a copy of the block has already
   754  						// been made and put into the dirty
   755  						// cache. (e.g., in a previous iteration of
   756  						// this loop).
   757  						pblock, _, err = fd.getter(
   758  							ctx, fd.tree.kmd, parentInfo.BlockPointer,
   759  							fd.tree.file, BlockWrite)
   760  						if err != nil {
   761  							return DirEntry{}, nil, nil,
   762  								newlyDirtiedChildBytes, err
   763  						}
   764  						pblock.IPtrs = pblock.IPtrs[:removeStartingFromIndex]
   765  						err = fd.tree.cacher(
   766  							ctx, parentInfo.BlockPointer, pblock)
   767  						if err != nil {
   768  							return DirEntry{}, nil, nil,
   769  								newlyDirtiedChildBytes, err
   770  						}
   771  						dirtyMap[parentInfo.BlockPointer] = true
   772  					}
   773  				}
   774  
   775  				// Down to the next level.  If we've hit the leaf
   776  				// level, unreference the block.
   777  				parentInfo = ptrs[pb.childIndex].BlockInfo
   778  				if i == len(Path)-1 && parentInfo.EncodedSize != 0 {
   779  					unrefs = append(unrefs, parentInfo)
   780  				} else if pb.childIndex > 0 {
   781  					leftMost = false
   782  				}
   783  			}
   784  		}
   785  	}
   786  
   787  	if topBlock.IsInd {
   788  		// Always make the top block dirty, so we will sync its
   789  		// indirect blocks.  This has the added benefit of ensuring
   790  		// that any truncate to a file while it's being sync'd will be
   791  		// deferred, even if it's to a block that's not currently
   792  		// being sync'd, since this top-most block will always be in
   793  		// the dirtyFiles map.
   794  		err = fd.tree.cacher(ctx, fd.rootBlockPointer(), topBlock)
   795  		if err != nil {
   796  			return DirEntry{}, nil, nil, newlyDirtiedChildBytes, err
   797  		}
   798  		dirtyMap[fd.rootBlockPointer()] = true
   799  	}
   800  
   801  	newDe = oldDe
   802  	newDe.EncodedSize = 0
   803  	newDe.Size = size
   804  
   805  	// Keep the old block ID while it's dirty.
   806  	if err = fd.tree.cacher(ctx, ptr, block); err != nil {
   807  		return DirEntry{}, nil, nil, newlyDirtiedChildBytes, err
   808  	}
   809  	dirtyMap[ptr] = true
   810  
   811  	dirtyPtrs = make([]BlockPointer, 0, len(dirtyMap))
   812  	for p := range dirtyMap {
   813  		dirtyPtrs = append(dirtyPtrs, p)
   814  	}
   815  
   816  	return newDe, dirtyPtrs, unrefs, newlyDirtiedChildBytes, nil
   817  }
   818  
   819  func (fd *FileData) getNextDirtyFileBlockAtOffset(ctx context.Context,
   820  	topBlock *FileBlock, off Int64Offset, rtype BlockReqType,
   821  	dirtyBcache DirtyBlockCache) (
   822  	ptr BlockPointer, parentBlocks []ParentBlockAndChildIndex,
   823  	block *FileBlock, nextBlockStartOff, startOff Int64Offset, err error) {
   824  	ptr, parentBlocks, b, nbso, so, err := fd.tree.getNextDirtyBlockAtOffset(
   825  		ctx, topBlock, off, rtype, dirtyBcache)
   826  	if err != nil {
   827  		return ZeroPtr, nil, nil, 0, 0, err
   828  	}
   829  	if b != nil {
   830  		block = b.(*FileBlock)
   831  	}
   832  	if nbso != nil {
   833  		nextBlockStartOff = nbso.(Int64Offset)
   834  	} else {
   835  		nextBlockStartOff = -1
   836  	}
   837  	if so != nil {
   838  		startOff = so.(Int64Offset)
   839  	}
   840  	return ptr, parentBlocks, block, nextBlockStartOff, startOff, nil
   841  }
   842  
   843  // Split checks, if given an indirect top block of a file, whether any
   844  // of the dirty leaf blocks in that file need to be split up
   845  // differently (i.e., if the BlockSplitter is using
   846  // fingerprinting-based boundaries).  It returns the set of blocks
   847  // that now need to be unreferenced.
   848  func (fd *FileData) Split(ctx context.Context, id tlf.ID,
   849  	dirtyBcache DirtyBlockCache, topBlock *FileBlock, df *DirtyFile) (
   850  	unrefs []BlockInfo, err error) {
   851  	if !topBlock.IsInd {
   852  		return nil, nil
   853  	}
   854  
   855  	// For an indirect file:
   856  	//   1) check if each dirty block is split at the right place.
   857  	//   2) if it needs fewer bytes, prepend the extra bytes to the next
   858  	//      block (making a new one if it doesn't exist), and the next block
   859  	//      gets marked dirty
   860  	//   3) if it needs more bytes, then use copyUntilSplit() to fetch bytes
   861  	//      from the next block (if there is one), remove the copied bytes
   862  	//      from the next block and mark it dirty
   863  	//   4) Then go through once more, and ready and finalize each
   864  	//      dirty block, updating its ID in the indirect pointer list
   865  	off := Int64Offset(0)
   866  	for off >= 0 {
   867  		_, parentBlocks, block, nextBlockOff, startOff, err :=
   868  			fd.getNextDirtyFileBlockAtOffset(
   869  				ctx, topBlock, off, BlockWrite, dirtyBcache)
   870  		if err != nil {
   871  			return unrefs, err
   872  		}
   873  
   874  		if block == nil {
   875  			// No more dirty blocks.
   876  			break
   877  		}
   878  		off = nextBlockOff // Will be -1 if there are no more blocks.
   879  
   880  		splitAt := fd.tree.bsplit.CheckSplit(block)
   881  		switch {
   882  		case splitAt == 0:
   883  			continue
   884  		case splitAt > 0:
   885  			endOfBlock := startOff + Int64Offset(len(block.Contents))
   886  			extraBytes := block.Contents[splitAt:]
   887  			block.Contents = block.Contents[:splitAt]
   888  			// put the extra bytes in front of the next block
   889  			if nextBlockOff < 0 {
   890  				// Need to make a new block.
   891  				if _, _, err := fd.tree.newRightBlock(
   892  					ctx, parentBlocks, endOfBlock,
   893  					DefaultNewBlockDataVersion(false), NewFileBlockWithPtrs,
   894  					fd.fileTopBlocker(df)); err != nil {
   895  					return unrefs, err
   896  				}
   897  			}
   898  			rPtr, rParentBlocks, rblock, _, _, _, err :=
   899  				fd.GetFileBlockAtOffset(
   900  					ctx, topBlock, endOfBlock, BlockWrite)
   901  			if err != nil {
   902  				return unrefs, err
   903  			}
   904  			rblock.Contents = append(extraBytes, rblock.Contents...)
   905  			if err = fd.tree.cacher(ctx, rPtr, rblock); err != nil {
   906  				return unrefs, err
   907  			}
   908  			endOfBlock = startOff + Int64Offset(len(block.Contents))
   909  
   910  			// Mark the old rblock as unref'd.
   911  			pb := rParentBlocks[len(rParentBlocks)-1]
   912  			childInfo, _ := pb.childIPtr()
   913  			unrefs = append(unrefs, childInfo)
   914  			pb.clearEncodedSize()
   915  
   916  			// Update parent pointer offsets as needed.
   917  			for i := len(rParentBlocks) - 1; i >= 0; i-- {
   918  				pb := rParentBlocks[i]
   919  				pb.pblock.(*FileBlock).IPtrs[pb.childIndex].Off = endOfBlock
   920  				// If this isn't the leftmost child at this level,
   921  				// there's no need to update the parent.
   922  				if pb.childIndex > 0 {
   923  					break
   924  				}
   925  			}
   926  
   927  			_, newUnrefs, err := fd.tree.markParentsDirty(ctx, rParentBlocks)
   928  			unrefs = append(unrefs, newUnrefs...)
   929  			if err != nil {
   930  				return unrefs, err
   931  			}
   932  			off = endOfBlock
   933  		case splitAt < 0:
   934  			if nextBlockOff < 0 {
   935  				// End of the line.
   936  				continue
   937  			}
   938  
   939  			endOfBlock := startOff + Int64Offset(len(block.Contents))
   940  			rPtr, rParentBlocks, rblock, _, _, _, err :=
   941  				fd.GetFileBlockAtOffset(
   942  					ctx, topBlock, endOfBlock, BlockWrite)
   943  			if err != nil {
   944  				return unrefs, err
   945  			}
   946  			// Copy some of that block's data into this block.
   947  			nCopied := fd.tree.bsplit.CopyUntilSplit(block, false,
   948  				rblock.Contents, int64(len(block.Contents)))
   949  			rblock.Contents = rblock.Contents[nCopied:]
   950  			endOfBlock = startOff + Int64Offset(len(block.Contents))
   951  
   952  			// Mark the old right block as unref'd.
   953  			pb := rParentBlocks[len(rParentBlocks)-1]
   954  			pblock := pb.pblock.(*FileBlock)
   955  			childInfo, _ := pb.childIPtr()
   956  			unrefs = append(unrefs, childInfo)
   957  			pb.clearEncodedSize()
   958  
   959  			// For the right block, adjust offset or delete as needed.
   960  			if len(rblock.Contents) > 0 {
   961  				if err = fd.tree.cacher(ctx, rPtr, rblock); err != nil {
   962  					return unrefs, err
   963  				}
   964  
   965  				// Update parent pointer offsets as needed.
   966  				for i := len(rParentBlocks) - 1; i >= 0; i-- {
   967  					pb := rParentBlocks[i]
   968  					pb.pblock.(*FileBlock).IPtrs[pb.childIndex].Off = endOfBlock
   969  					// If this isn't the leftmost child at this level,
   970  					// there's no need to update the parent.
   971  					if pb.childIndex > 0 {
   972  						break
   973  					}
   974  				}
   975  			} else {
   976  				// TODO: If we're down to just one leaf block at this
   977  				// level, remove the layer of indirection (KBFS-1824).
   978  				iptrs := pblock.IPtrs
   979  				pblock.IPtrs = make([]IndirectFilePtr, len(iptrs)-1)
   980  				copy(pblock.IPtrs, iptrs[:pb.childIndex])
   981  				copy(pblock.IPtrs[pb.childIndex:], iptrs[pb.childIndex+1:])
   982  			}
   983  
   984  			// Mark all parents as dirty.
   985  			_, newUnrefs, err := fd.tree.markParentsDirty(ctx, rParentBlocks)
   986  			unrefs = append(unrefs, newUnrefs...)
   987  			if err != nil {
   988  				return unrefs, err
   989  			}
   990  
   991  			off = endOfBlock
   992  		}
   993  	}
   994  	return unrefs, nil
   995  }
   996  
   997  // Ready readies, if given an indirect top-block, all the dirty child
   998  // blocks, and updates their block IDs in their parent block's list of
   999  // indirect pointers.  It returns a map pointing from the new block
  1000  // info from any readied block to its corresponding old block pointer.
  1001  func (fd *FileData) Ready(ctx context.Context, id tlf.ID,
  1002  	bcache BlockCache, dirtyBcache IsDirtyProvider,
  1003  	rp ReadyProvider, bps BlockPutState, topBlock *FileBlock, df *DirtyFile,
  1004  	hashBehavior BlockCacheHashBehavior) (
  1005  	map[BlockInfo]BlockPointer, error) {
  1006  	return fd.tree.ready(
  1007  		ctx, id, bcache, dirtyBcache, rp, bps, topBlock,
  1008  		func(ptr BlockPointer) func() error {
  1009  			if df != nil {
  1010  				return func() error { return df.setBlockSynced(ptr) }
  1011  			}
  1012  			return nil
  1013  		}, hashBehavior)
  1014  }
  1015  
  1016  // GetIndirectFileBlockInfosWithTopBlock returns the block infos
  1017  // contained in all the indirect blocks in this file tree, given an
  1018  // already-fetched top block.
  1019  func (fd *FileData) GetIndirectFileBlockInfosWithTopBlock(
  1020  	ctx context.Context, topBlock *FileBlock) ([]BlockInfo, error) {
  1021  	return fd.tree.getIndirectBlockInfosWithTopBlock(ctx, topBlock)
  1022  }
  1023  
  1024  // GetIndirectFileBlockInfos returns the block infos contained in all
  1025  // the indirect blocks in this file tree.
  1026  func (fd *FileData) GetIndirectFileBlockInfos(ctx context.Context) (
  1027  	[]BlockInfo, error) {
  1028  	return fd.tree.getIndirectBlockInfos(ctx)
  1029  }
  1030  
  1031  // FindIPtrsAndClearSize looks for the given set of indirect pointers,
  1032  // and returns whether they could be found.  As a side effect, it also
  1033  // clears the encoded size for those indirect pointers.
  1034  func (fd *FileData) FindIPtrsAndClearSize(
  1035  	ctx context.Context, topBlock *FileBlock, ptrs map[BlockPointer]bool) (
  1036  	found map[BlockPointer]bool, err error) {
  1037  	if !topBlock.IsInd || len(ptrs) == 0 {
  1038  		return nil, nil
  1039  	}
  1040  
  1041  	pfr, err := fd.tree.getIndirectBlocksForOffsetRange(
  1042  		ctx, topBlock, Int64Offset(0), nil)
  1043  	if err != nil {
  1044  		return nil, err
  1045  	}
  1046  
  1047  	found = make(map[BlockPointer]bool)
  1048  
  1049  	// Search all paths for the given block pointer, clear its encoded
  1050  	// size, and dirty all its parents up to the root.
  1051  	infoSeen := make(map[BlockPointer]bool)
  1052  	for _, Path := range pfr {
  1053  		parentPtr := fd.rootBlockPointer()
  1054  		for level, pb := range Path {
  1055  			if infoSeen[parentPtr] {
  1056  				parentPtr = pb.childBlockPtr()
  1057  				continue
  1058  			}
  1059  			infoSeen[parentPtr] = true
  1060  
  1061  			for i, iptr := range pb.pblock.(*FileBlock).IPtrs {
  1062  				if ptrs[iptr.BlockPointer] {
  1063  					// Mark this pointer, and all parent blocks, as dirty.
  1064  					parentPtr := fd.rootBlockPointer()
  1065  					for i := 0; i <= level; i++ {
  1066  						// Get a writeable copy for each block.
  1067  						pblock, _, err := fd.getter(
  1068  							ctx, fd.tree.kmd, parentPtr, fd.tree.file,
  1069  							BlockWrite)
  1070  						if err != nil {
  1071  							return nil, err
  1072  						}
  1073  						Path[i].pblock = pblock
  1074  						parentPtr = Path[i].childBlockPtr()
  1075  					}
  1076  					// Because we only check each parent once, the
  1077  					// `path` we're using here will be the one with a
  1078  					// childIndex of 0.  But, that's not necessarily
  1079  					// the one that matches the pointer that needs to
  1080  					// be dirty.  So make a new path and set the
  1081  					// childIndex to the correct pointer instead.
  1082  					newPath := make([]ParentBlockAndChildIndex, level+1)
  1083  					copy(newPath, Path[:level+1])
  1084  					newPath[level].childIndex = i
  1085  					_, _, err = fd.tree.markParentsDirty(ctx, newPath)
  1086  					if err != nil {
  1087  						return nil, err
  1088  					}
  1089  
  1090  					found[iptr.BlockPointer] = true
  1091  					if len(found) == len(ptrs) {
  1092  						return found, nil
  1093  					}
  1094  				}
  1095  			}
  1096  			parentPtr = pb.childBlockPtr()
  1097  		}
  1098  	}
  1099  	return found, nil
  1100  }
  1101  
  1102  // DeepCopy makes a complete copy of this file, deduping leaf blocks
  1103  // and making new random BlockPointers for all indirect blocks.  It
  1104  // returns the new top pointer of the copy, and all the new child
  1105  // pointers in the copy.
  1106  func (fd *FileData) DeepCopy(ctx context.Context, dataVer Ver) (
  1107  	newTopPtr BlockPointer, allChildPtrs []BlockPointer, err error) {
  1108  	topBlock, _, err := fd.getter(ctx, fd.tree.kmd, fd.rootBlockPointer(),
  1109  		fd.tree.file, BlockRead)
  1110  	if err != nil {
  1111  		return ZeroPtr, nil, err
  1112  	}
  1113  
  1114  	// Handle the single-level case first.
  1115  	if !topBlock.IsInd {
  1116  		newTopBlock := topBlock.DeepCopy()
  1117  
  1118  		newTopPtr = fd.rootBlockPointer()
  1119  		newTopPtr.RefNonce, err = kbfsblock.MakeRefNonce()
  1120  		if err != nil {
  1121  			return ZeroPtr, nil, err
  1122  		}
  1123  		newTopPtr.SetWriter(fd.tree.chargedTo)
  1124  
  1125  		if err = fd.tree.cacher(ctx, newTopPtr, newTopBlock); err != nil {
  1126  			return ZeroPtr, nil, err
  1127  		}
  1128  
  1129  		fd.tree.vlog.CLogf(
  1130  			ctx, libkb.VLog1, "Deep copied file %s: %v -> %v",
  1131  			fd.tree.file.TailName(), fd.rootBlockPointer(), newTopPtr)
  1132  
  1133  		return newTopPtr, nil, nil
  1134  	}
  1135  
  1136  	// For indirect files, get all the paths to leaf blocks.
  1137  	pfr, err := fd.tree.getIndirectBlocksForOffsetRange(
  1138  		ctx, topBlock, Int64Offset(0), nil)
  1139  	if err != nil {
  1140  		return ZeroPtr, nil, err
  1141  	}
  1142  	if len(pfr) == 0 {
  1143  		return ZeroPtr, nil,
  1144  			fmt.Errorf("Indirect file %v had no indirect blocks",
  1145  				fd.rootBlockPointer())
  1146  	}
  1147  
  1148  	// Make a new reference for all leaf blocks first.
  1149  	copiedBlocks := make(map[BlockPointer]*FileBlock)
  1150  	leafLevel := len(pfr[0]) - 1
  1151  	for level := leafLevel; level >= 0; level-- {
  1152  		for _, Path := range pfr {
  1153  			// What is the current ptr for this pblock?
  1154  			ptr := fd.rootBlockPointer()
  1155  			if level > 0 {
  1156  				ptr = Path[level-1].childBlockPtr()
  1157  			}
  1158  			if _, ok := copiedBlocks[ptr]; ok {
  1159  				continue
  1160  			}
  1161  
  1162  			// Copy the parent block and save it for later (it will be
  1163  			// cached below).
  1164  			pblock := Path[level].pblock.(*FileBlock).DeepCopy()
  1165  			if err != nil {
  1166  				return ZeroPtr, nil, err
  1167  			}
  1168  			copiedBlocks[ptr] = pblock
  1169  
  1170  			for i, iptr := range pblock.IPtrs {
  1171  				if level == leafLevel {
  1172  					// Generate a new nonce for each indirect pointer
  1173  					// to a leaf.
  1174  					iptr.RefNonce, err = kbfsblock.MakeRefNonce()
  1175  					if err != nil {
  1176  						return ZeroPtr, nil, err
  1177  					}
  1178  					iptr.SetWriter(fd.tree.chargedTo)
  1179  					pblock.IPtrs[i] = iptr
  1180  					allChildPtrs = append(allChildPtrs, iptr.BlockPointer)
  1181  				} else {
  1182  					// Generate a new random ID for each indirect
  1183  					// pointer to an indirect block.
  1184  					newID, err := kbfsblock.MakeTemporaryID()
  1185  					if err != nil {
  1186  						return ZeroPtr, nil, err
  1187  					}
  1188  					// No need for a new refnonce here, since indirect
  1189  					// blocks are guaranteed to get a new block ID
  1190  					// when readied, since the child block pointers
  1191  					// will have changed.
  1192  					newPtr := BlockPointer{
  1193  						ID:      newID,
  1194  						KeyGen:  fd.tree.kmd.LatestKeyGeneration(),
  1195  						DataVer: dataVer,
  1196  						Context: kbfsblock.MakeFirstContext(
  1197  							fd.tree.chargedTo,
  1198  							fd.rootBlockPointer().GetBlockType()),
  1199  						DirectType: IndirectBlock,
  1200  					}
  1201  					pblock.IPtrs[i].BlockPointer = newPtr
  1202  					allChildPtrs = append(allChildPtrs, newPtr)
  1203  					childBlock, ok := copiedBlocks[iptr.BlockPointer]
  1204  					if !ok {
  1205  						return ZeroPtr, nil, fmt.Errorf(
  1206  							"No copied child block found for ptr %v",
  1207  							iptr.BlockPointer)
  1208  					}
  1209  					err = fd.tree.cacher(ctx, newPtr, childBlock)
  1210  					if err != nil {
  1211  						return ZeroPtr, nil, err
  1212  					}
  1213  				}
  1214  			}
  1215  		}
  1216  	}
  1217  
  1218  	// Finally, make a new ID for the top block and cache it.
  1219  	newTopPtr = fd.rootBlockPointer()
  1220  	newID, err := kbfsblock.MakeTemporaryID()
  1221  	if err != nil {
  1222  		return ZeroPtr, nil, err
  1223  	}
  1224  	newTopPtr = BlockPointer{
  1225  		ID:      newID,
  1226  		KeyGen:  fd.tree.kmd.LatestKeyGeneration(),
  1227  		DataVer: dataVer,
  1228  		Context: kbfsblock.MakeFirstContext(
  1229  			fd.tree.chargedTo, fd.rootBlockPointer().GetBlockType()),
  1230  		DirectType: IndirectBlock,
  1231  	}
  1232  	fd.tree.vlog.CLogf(
  1233  		ctx, libkb.VLog1, "Deep copied indirect file %s: %v -> %v",
  1234  		fd.tree.file.TailName(), fd.rootBlockPointer(), newTopPtr)
  1235  
  1236  	newTopBlock, ok := copiedBlocks[fd.rootBlockPointer()]
  1237  	if !ok {
  1238  		return ZeroPtr, nil, fmt.Errorf(
  1239  			"No copied root block found for ptr %v",
  1240  			fd.rootBlockPointer())
  1241  	}
  1242  	if err = fd.tree.cacher(ctx, newTopPtr, newTopBlock); err != nil {
  1243  		return ZeroPtr, nil, err
  1244  	}
  1245  
  1246  	return newTopPtr, allChildPtrs, nil
  1247  }
  1248  
  1249  // UndupChildrenInCopy takes a top block that's been copied via
  1250  // deepCopy(), and un-deduplicates all leaf children of the block.  It
  1251  // adds all child blocks to the provided `bps`, including both the
  1252  // ones that were deduplicated and the ones that weren't.  It returns
  1253  // the BlockInfos for all children.
  1254  func (fd *FileData) UndupChildrenInCopy(ctx context.Context,
  1255  	bcache BlockCache, rp ReadyProvider, bps BlockPutState,
  1256  	topBlock *FileBlock, hashBehavior BlockCacheHashBehavior) (
  1257  	[]BlockInfo, error) {
  1258  	if !topBlock.IsInd {
  1259  		return nil, nil
  1260  	}
  1261  
  1262  	// For indirect files, get all the paths to leaf blocks.  Note
  1263  	// that because topBlock is a result of `deepCopy`, all of the
  1264  	// indirect blocks that will make up the paths are also deep
  1265  	// copies, and thus are modifiable.
  1266  	pfr, err := fd.tree.getIndirectBlocksForOffsetRange(
  1267  		ctx, topBlock, Int64Offset(0), nil)
  1268  	if err != nil {
  1269  		return nil, err
  1270  	}
  1271  	if len(pfr) == 0 {
  1272  		return nil, fmt.Errorf(
  1273  			"Indirect file %v had no indirect blocks", fd.rootBlockPointer())
  1274  	}
  1275  
  1276  	// Append the leaf block to each path, since readyHelper expects it.
  1277  	// TODO: parallelize these fetches.
  1278  	for i, Path := range pfr {
  1279  		leafPtr := Path[len(Path)-1].childBlockPtr()
  1280  		leafBlock, _, err := fd.getter(
  1281  			ctx, fd.tree.kmd, leafPtr, fd.tree.file, BlockWrite)
  1282  		if err != nil {
  1283  			return nil, err
  1284  		}
  1285  
  1286  		pfr[i] = append(pfr[i], ParentBlockAndChildIndex{leafBlock, -1})
  1287  	}
  1288  
  1289  	newInfos, err := fd.tree.readyHelper(
  1290  		ctx, fd.tree.file.Tlf, bcache, rp, bps, pfr, nil, hashBehavior)
  1291  	if err != nil {
  1292  		return nil, err
  1293  	}
  1294  
  1295  	blockInfos := make([]BlockInfo, 0, len(newInfos))
  1296  	for newInfo := range newInfos {
  1297  		blockInfos = append(blockInfos, newInfo)
  1298  	}
  1299  	return blockInfos, nil
  1300  }
  1301  
  1302  // ReadyNonLeafBlocksInCopy takes a top block that's been copied via
  1303  // deepCopy(), and readies all the non-leaf children of the top block.
  1304  // It adds all readied blocks to the provided `bps`.  It returns the
  1305  // BlockInfos for all non-leaf children.
  1306  func (fd *FileData) ReadyNonLeafBlocksInCopy(ctx context.Context,
  1307  	bcache BlockCache, rp ReadyProvider, bps BlockPutState,
  1308  	topBlock *FileBlock, hashBehavior BlockCacheHashBehavior) (
  1309  	[]BlockInfo, error) {
  1310  	if !topBlock.IsInd {
  1311  		return nil, nil
  1312  	}
  1313  
  1314  	// For indirect files, get all the paths to leaf blocks.  Note
  1315  	// that because topBlock is a deepCopy, all of the blocks are also
  1316  	// deepCopys and thus are modifiable.
  1317  	pfr, err := fd.tree.getIndirectBlocksForOffsetRange(
  1318  		ctx, topBlock, Int64Offset(0), nil)
  1319  	if err != nil {
  1320  		return nil, err
  1321  	}
  1322  	if len(pfr) == 0 {
  1323  		return nil, fmt.Errorf(
  1324  			"Indirect file %v had no indirect blocks", fd.rootBlockPointer())
  1325  	}
  1326  
  1327  	newInfos, err := fd.tree.readyHelper(
  1328  		ctx, fd.tree.file.Tlf, bcache, rp, bps, pfr, nil, hashBehavior)
  1329  	if err != nil {
  1330  		return nil, err
  1331  	}
  1332  
  1333  	blockInfos := make([]BlockInfo, 0, len(newInfos))
  1334  	for newInfo := range newInfos {
  1335  		blockInfos = append(blockInfos, newInfo)
  1336  	}
  1337  	return blockInfos, nil
  1338  }