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 }