github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/data_types.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "context" 9 "encoding/json" 10 "fmt" 11 "strings" 12 "time" 13 14 "github.com/keybase/client/go/kbfs/data" 15 "github.com/keybase/client/go/kbfs/kbfscodec" 16 "github.com/keybase/client/go/kbfs/kbfscrypto" 17 "github.com/keybase/client/go/kbfs/kbfsmd" 18 "github.com/keybase/client/go/kbfs/tlf" 19 kbname "github.com/keybase/client/go/kbun" 20 kbgitkbfs "github.com/keybase/client/go/protocol/kbgitkbfs1" 21 "github.com/keybase/client/go/protocol/keybase1" 22 "github.com/keybase/go-codec/codec" 23 "github.com/pkg/errors" 24 ) 25 26 // disallowedPrefixes must not be allowed at the beginning of any 27 // user-created directory entry name. 28 var disallowedPrefixes = [...]string{".kbfs"} 29 30 // EncryptedTLFCryptKeyClientAndEphemeral has what's needed to 31 // request a client half decryption. 32 type EncryptedTLFCryptKeyClientAndEphemeral struct { 33 // PublicKey contains the wrapped Key ID of the public key 34 PubKey kbfscrypto.CryptPublicKey 35 // ClientHalf contains the encrypted client half of the TLF key 36 ClientHalf kbfscrypto.EncryptedTLFCryptKeyClientHalf 37 // EPubKey contains the ephemeral public key used to encrypt ClientHalf 38 EPubKey kbfscrypto.TLFEphemeralPublicKey 39 } 40 41 const ( 42 defaultClientMetadataVer kbfsmd.MetadataVer = kbfsmd.ImplicitTeamsVer 43 ) 44 45 // BlockChanges tracks the set of blocks that changed in a commit, and 46 // the operations that made the changes. It might consist of just a 47 // BlockPointer if the list is too big to embed in the MD structure 48 // directly. 49 // 50 // If this commit represents a conflict-resolution merge, which may 51 // comprise multiple individual operations, then there will be an 52 // ordered list of the changes for individual operations. This lets 53 // the notification and conflict resolution strategies figure out the 54 // difference between a renamed file and a modified file, for example. 55 // 56 // NOTE: Don't add or modify anything in this struct without 57 // considering how old clients will handle them. 58 type BlockChanges struct { 59 // If this is set, the actual changes are stored in a block (where 60 // the block contains a serialized version of BlockChanges) 61 // 62 // Ideally, we'd omit Info if it's empty. However, old clients 63 // rely on encoded BlockChanges always having an encoded Info, 64 // so that decoding into an existing BlockChanges object 65 // clobbers any existing Info, so we can't omit Info until all 66 // clients have upgraded to a version that explicitly clears 67 // Info on decode, and we've verified that there's nothing 68 // else that relies on Info always being filled. 69 Info data.BlockInfo `codec:"p"` 70 // An ordered list of operations completed in this update 71 Ops opsList `codec:"o,omitempty"` 72 // Estimate the number of bytes that this set of changes will take to encode 73 sizeEstimate uint64 74 } 75 76 // Equals returns true if the given BlockChanges is equal to this 77 // BlockChanges. Currently does not check for equality at the 78 // operation level. 79 func (bc BlockChanges) Equals(other BlockChanges) bool { 80 if bc.Info != other.Info || len(bc.Ops) != len(other.Ops) || 81 (bc.sizeEstimate != 0 && other.sizeEstimate != 0 && 82 bc.sizeEstimate != other.sizeEstimate) { 83 return false 84 } 85 // TODO: check for op equality? 86 return true 87 } 88 89 // AddRefBlock adds the newly-referenced block to this BlockChanges 90 // and updates the size estimate. 91 func (bc *BlockChanges) AddRefBlock(ptr data.BlockPointer) { 92 if bc.sizeEstimate != 0 { 93 panic("Can't alter block changes after the size is estimated") 94 } 95 bc.Ops[len(bc.Ops)-1].AddRefBlock(ptr) 96 } 97 98 // AddUnrefBlock adds the newly unreferenced block to this BlockChanges 99 // and updates the size estimate. 100 func (bc *BlockChanges) AddUnrefBlock(ptr data.BlockPointer) { 101 if bc.sizeEstimate != 0 { 102 panic("Can't alter block changes after the size is estimated") 103 } 104 bc.Ops[len(bc.Ops)-1].AddUnrefBlock(ptr) 105 } 106 107 // AddUpdate adds the newly updated block to this BlockChanges 108 // and updates the size estimate. 109 func (bc *BlockChanges) AddUpdate(oldPtr data.BlockPointer, newPtr data.BlockPointer) { 110 if bc.sizeEstimate != 0 { 111 panic("Can't alter block changes after the size is estimated") 112 } 113 bc.Ops[len(bc.Ops)-1].AddUpdate(oldPtr, newPtr) 114 } 115 116 // AddOp starts a new operation for this BlockChanges. Subsequent 117 // Add* calls will populate this operation. 118 func (bc *BlockChanges) AddOp(o op) { 119 if bc.sizeEstimate != 0 { 120 panic("Can't alter block changes after the size is estimated") 121 } 122 bc.Ops = append(bc.Ops, o) 123 } 124 125 // SizeEstimate calculates the estimated size of the encoded version 126 // of this BlockChanges. 127 func (bc *BlockChanges) SizeEstimate() uint64 { 128 if bc.sizeEstimate == 0 { 129 for _, op := range bc.Ops { 130 numPtrs := len(op.Refs()) + len(op.Unrefs()) + 131 2*len(op.allUpdates()) 132 bc.sizeEstimate += 133 uint64(numPtrs)*data.BPSize + op.SizeExceptUpdates() 134 } 135 } 136 return bc.sizeEstimate 137 } 138 139 // Excl indicates whether O_EXCL is set on a fuse call 140 type Excl bool 141 142 const ( 143 // NoExcl indicates O_EXCL is not set 144 NoExcl Excl = false 145 146 // WithExcl indicates O_EXCL is set 147 WithExcl Excl = true 148 ) 149 150 func (o Excl) String() string { 151 switch o { 152 case NoExcl: 153 return "O_EXCL unset" 154 case WithExcl: 155 return "O_EXCL set" 156 default: 157 return "<invalid Excl>" 158 } 159 } 160 161 // ReportedError represents an error reported by KBFS. 162 type ReportedError struct { 163 Time time.Time 164 Error error 165 Stack []uintptr 166 } 167 168 // OpSummary describes the changes performed by a single op, and is 169 // suitable for encoding directly as JSON. 170 type OpSummary struct { 171 Op string 172 Refs []string 173 Unrefs []string 174 Updates map[string]string 175 } 176 177 // UpdateSummary describes the operations done by a single MD revision. 178 type UpdateSummary struct { 179 Revision kbfsmd.Revision 180 Date time.Time 181 Writer string 182 LiveBytes uint64 // the "DiskUsage" for the TLF as of this revision 183 Ops []OpSummary 184 RootBlockID string 185 } 186 187 // TLFUpdateHistory gives all the summaries of all updates in a TLF's 188 // history. 189 type TLFUpdateHistory struct { 190 ID string 191 Name string 192 Updates []UpdateSummary 193 } 194 195 // writerInfo is the keybase UID and device (represented by its 196 // verifying key) that generated the operation at the given revision. 197 type writerInfo struct { 198 uid keybase1.UID 199 key kbfscrypto.VerifyingKey 200 revision kbfsmd.Revision 201 offline keybase1.OfflineAvailability 202 } 203 204 // ErrorModeType indicates what type of operation was being attempted 205 // when an error was reported. 206 type ErrorModeType int 207 208 const ( 209 // ReadMode indicates that an error happened while trying to read. 210 ReadMode ErrorModeType = iota 211 // WriteMode indicates that an error happened while trying to write. 212 WriteMode 213 ) 214 215 // NodeMetadata has metadata about a node needed for higher level operations. 216 type NodeMetadata struct { 217 // LastWriterUnverified is the last writer of this 218 // node according to the last writer of the TLF. 219 // A more thorough check is possible in the future. 220 LastWriterUnverified kbname.NormalizedUsername 221 BlockInfo data.BlockInfo 222 PrefetchStatus PrefetchStatus 223 PrefetchProgress *PrefetchProgress `json:",omitempty"` 224 } 225 226 // FavoritesOp defines an operation related to favorites. 227 type FavoritesOp int 228 229 const ( 230 _ FavoritesOp = iota 231 // FavoritesOpAdd means TLF should be added to favorites. 232 FavoritesOpAdd 233 // FavoritesOpAddNewlyCreated means TLF should be added to favorites, and it 234 // should be considered newly created. 235 FavoritesOpAddNewlyCreated 236 // FavoritesOpRemove means TLF should be removed from favorites. 237 FavoritesOpRemove 238 // FavoritesOpNoChange means no changes regarding to favorites should be made. 239 FavoritesOpNoChange 240 ) 241 242 // RekeyResult represents the result of an rekey operation. 243 type RekeyResult struct { 244 DidRekey bool 245 NeedsPaperKey bool 246 } 247 248 // InitModeType indicates how KBFS should configure itself at runtime. 249 type InitModeType int 250 251 const ( 252 // InitDefault is the normal mode for when KBFS data will be read 253 // and written. 254 InitDefault InitModeType = iota 255 // InitMinimal is for when KBFS will only be used as a MD lookup 256 // layer (e.g., for chat on mobile). 257 InitMinimal 258 // InitSingleOp is a mode for when KBFS is only needed for a 259 // single logical operation; no rekeys or update subscriptions is 260 // needed, and some naming restrictions are lifted (e.g., `.kbfs_` 261 // filenames are allowed). 262 InitSingleOp 263 // InitConstrained is a mode where KBFS reads and writes data, but 264 // constrains itself to using fewer resources (e.g. on mobile). 265 InitConstrained 266 // InitMemoryLimited is a mode where KBFS reads and writes data, but 267 // constrains its memory use even further. 268 InitMemoryLimited 269 // InitTestSearch is the same as the default mode, but with search 270 // enabled for synced TLFs. 271 InitTestSearch 272 // InitSingleOpWithQR is the same as InitSingleOp, except quota 273 // reclamation is enabled. That way if the user of the mode 274 // writes data to a TLF that exclusive to the mode, it will still 275 // be QR'd. (Example: the indexer.) 276 InitSingleOpWithQR 277 ) 278 279 func (im InitModeType) String() string { 280 switch im { 281 case InitDefault: 282 return InitDefaultString 283 case InitMinimal: 284 return InitMinimalString 285 case InitSingleOp: 286 return InitSingleOpString 287 case InitConstrained: 288 return InitConstrainedString 289 case InitMemoryLimited: 290 return InitMemoryLimitedString 291 case InitTestSearch: 292 return InitTestSearchString 293 case InitSingleOpWithQR: 294 return InitSingleOpWithQRString 295 default: 296 return "unknown" 297 } 298 } 299 300 // PrefetchStatus denotes the prefetch status of a block. 301 type PrefetchStatus int 302 303 // ErrUnrecognizedPrefetchStatus is returned when trying to unmarshal a 304 // prefetch status from JSON if the prefetch status is unrecognized. 305 var ErrUnrecognizedPrefetchStatus = errors.New( 306 "Unrecognized PrefetchStatus value") 307 308 const ( 309 // NoPrefetch represents an entry that hasn't been prefetched. 310 NoPrefetch PrefetchStatus = iota 311 // TriggeredPrefetch represents a block for which prefetching has been 312 // triggered, but the full tree has not been completed. 313 TriggeredPrefetch 314 // FinishedPrefetch represents a block whose full subtree is synced. 315 FinishedPrefetch 316 ) 317 318 func (s PrefetchStatus) String() string { 319 switch s { 320 case NoPrefetch: 321 return "NoPrefetch" 322 case TriggeredPrefetch: 323 return "TriggeredPrefetch" 324 case FinishedPrefetch: 325 return "FinishedPrefetch" 326 } 327 return "Unknown" 328 } 329 330 // ToProtocolStatus returns a prefetch status that can be send over 331 // the keybase1 protocol. 332 func (s PrefetchStatus) ToProtocolStatus() keybase1.PrefetchStatus { 333 switch s { 334 case NoPrefetch: 335 return keybase1.PrefetchStatus_NOT_STARTED 336 case TriggeredPrefetch: 337 return keybase1.PrefetchStatus_IN_PROGRESS 338 case FinishedPrefetch: 339 return keybase1.PrefetchStatus_COMPLETE 340 default: 341 panic(fmt.Sprintf("Unknown prefetch status: %s", s)) 342 } 343 } 344 345 // MarshalJSON converts a PrefetchStatus to JSON 346 func (s PrefetchStatus) MarshalJSON() ([]byte, error) { 347 return json.Marshal(s.String()) 348 } 349 350 // UnmarshalJSON converts a PrefetchStatus from JSON 351 func (s *PrefetchStatus) UnmarshalJSON(b []byte) error { 352 var st string 353 if err := json.Unmarshal(b, &st); err != nil { 354 return err 355 } 356 switch st { 357 default: 358 return ErrUnrecognizedPrefetchStatus 359 case "NoPrefetch": 360 *s = NoPrefetch 361 case "TriggeredPrefetch": 362 *s = TriggeredPrefetch 363 case "FinishedPrefetch": 364 *s = FinishedPrefetch 365 } 366 return nil 367 } 368 369 // ToProtocol transforms a PrefetchStatus to a kbgitkbfs.PrefetchStatus, while 370 // validating its value. 371 func (s PrefetchStatus) ToProtocol() kbgitkbfs.PrefetchStatus { 372 protocolPrefetchStatus := kbgitkbfs.PrefetchStatus(s) 373 _, ok := kbgitkbfs.PrefetchStatusRevMap[protocolPrefetchStatus] 374 if !ok { 375 panic("Invalid prefetch status for protocol") 376 } 377 return protocolPrefetchStatus 378 } 379 380 // PrefetchStatusFromProtocol transforms a kbgitkbfs.PrefetchStatus to a 381 // PrefetchStatus, while validating its value. 382 func PrefetchStatusFromProtocol( 383 protocolPrefetchStatus kbgitkbfs.PrefetchStatus) PrefetchStatus { 384 s := PrefetchStatus(protocolPrefetchStatus) 385 switch s { 386 case NoPrefetch: 387 case TriggeredPrefetch: 388 case FinishedPrefetch: 389 default: 390 panic("Invalid prefetch status from protocol") 391 } 392 return s 393 } 394 395 // FolderSyncEncryptedPartialPaths describes an encrypted block 396 // containing the paths of a partial sync config. 397 type FolderSyncEncryptedPartialPaths struct { 398 Ptr data.BlockPointer 399 Buf []byte 400 ServerHalf kbfscrypto.BlockCryptKeyServerHalf 401 } 402 403 // FolderSyncConfig is the on-disk representation for a TLF sync 404 // config. 405 type FolderSyncConfig struct { 406 Mode keybase1.FolderSyncMode `codec:"mode" json:"mode"` 407 Paths FolderSyncEncryptedPartialPaths `codec:"paths" json:"paths"` 408 TlfPath string `codec:"tlfpath" json:"tlfpath"` 409 } 410 411 type syncPathList struct { 412 // Paths is a list of files and directories within a TLF that are 413 // configured to be synced to the local device. 414 Paths []string 415 416 codec.UnknownFieldSetHandler 417 } 418 419 func (spl syncPathList) makeBlock(codec kbfscodec.Codec) (data.Block, error) { 420 buf, err := codec.Encode(spl) 421 if err != nil { 422 return nil, err 423 } 424 b := data.NewFileBlock().(*data.FileBlock) 425 b.Contents = buf 426 return b, nil 427 } 428 429 func syncPathListFromBlock(codec kbfscodec.Codec, b *data.FileBlock) ( 430 paths syncPathList, err error) { 431 err = codec.Decode(b.Contents, &paths) 432 if err != nil { 433 return syncPathList{}, err 434 } 435 return paths, nil 436 } 437 438 // BlockMetadataValue represents the value stored in the block metadata 439 // store. This is usually locally stored, and is separate from block metadata 440 // stored on bserver. 441 type BlockMetadataValue struct { 442 // Xattr contains all xattrs stored in association with the block. This is 443 // useful for stuff that's contingent to content of the block, such as 444 // quarantine data. 445 Xattr map[XattrType][]byte 446 } 447 448 // BlockMetadataUpdater defines a function to update a BlockMetadataValue. 449 type BlockMetadataUpdater func(*BlockMetadataValue) error 450 451 // BlockRequestAction indicates what kind of action should be taken 452 // after successfully fetching a block. This is a bit mask filled 453 // with `blockRequestFlag`s. 454 type BlockRequestAction int 455 456 const ( 457 // These unexported actions are really flags that are combined to 458 // make the other actions below. 459 blockRequestTrackedInPrefetch BlockRequestAction = 1 << iota 460 blockRequestPrefetch 461 blockRequestSync 462 blockRequestStopIfFull 463 blockRequestStopPrefetchIfFull 464 blockRequestDeepSync 465 blockRequestDelayCacheCheck 466 blockRequestNonMasterBranch 467 468 // BlockRequestSolo indicates that no action should take place 469 // after fetching the block. However, a TLF that is configured to 470 // be fully-synced will still be prefetched and synced. 471 BlockRequestSolo BlockRequestAction = 0 472 // BlockRequestSoloWithSync indicates the the requested block 473 // should be put in the sync cache, but no prefetching should be 474 // triggered. 475 BlockRequestSoloWithSync BlockRequestAction = blockRequestSync 476 // BlockRequestPrefetchTail indicates that the block is being 477 // tracked in the prefetcher, but shouldn't kick off any more 478 // prefetches. 479 BlockRequestPrefetchTail BlockRequestAction = blockRequestTrackedInPrefetch 480 // BlockRequestPrefetchTailWithSync indicates that the block is 481 // being tracked in the prefetcher and goes in the sync cache, but 482 // shouldn't kick off any more prefetches. 483 BlockRequestPrefetchTailWithSync BlockRequestAction = blockRequestTrackedInPrefetch | blockRequestSync 484 // BlockRequestWithPrefetch indicates that a prefetch should be 485 // triggered after fetching the block. If a TLF is configured to 486 // be fully-synced, the block will still be put in the sync cache. 487 BlockRequestWithPrefetch BlockRequestAction = blockRequestTrackedInPrefetch | blockRequestPrefetch 488 // BlockRequestWithSyncAndPrefetch indicates that the block should 489 // be stored in the sync cache after fetching it, as well as 490 // triggering a prefetch of one level of child blocks (and the 491 // syncing doesn't propagate to the child blocks). 492 BlockRequestWithSyncAndPrefetch BlockRequestAction = blockRequestTrackedInPrefetch | blockRequestPrefetch | blockRequestSync 493 // BlockRequestPrefetchUntilFull prefetches starting from the 494 // given block (but does not sync the blocks) until the working 495 // set cache is full, and then it stops prefetching. 496 BlockRequestPrefetchUntilFull BlockRequestAction = blockRequestTrackedInPrefetch | blockRequestPrefetch | blockRequestStopIfFull 497 // BlockRequestWithDeepSync is the same as above, except both the 498 // prefetching and the sync flags propagate to the child, so the 499 // whole tree root at the block is prefetched and synced. 500 BlockRequestWithDeepSync BlockRequestAction = blockRequestTrackedInPrefetch | blockRequestPrefetch | blockRequestSync | blockRequestDeepSync 501 ) 502 503 func (bra BlockRequestAction) String() string { 504 if bra.DeepSync() { 505 return "deep-sync" 506 } 507 if bra == BlockRequestSolo { 508 return "solo" 509 } 510 511 attrs := make([]string, 0, 3) 512 if bra.prefetch() { 513 attrs = append(attrs, "prefetch") 514 } else if bra.PrefetchTracked() { 515 attrs = append(attrs, "prefetch-tracked") 516 } 517 518 if bra.Sync() { 519 attrs = append(attrs, "sync") 520 } 521 522 if bra.StopPrefetchIfFull() { 523 attrs = append(attrs, "stop-prefetch-if-full") 524 } else if bra.StopIfFull() { 525 attrs = append(attrs, "stop-if-full") 526 } 527 528 if bra.DelayCacheCheck() { 529 attrs = append(attrs, "delay-cache-check") 530 } 531 if bra.NonMasterBranch() { 532 attrs = append(attrs, "non-master-branch") 533 } 534 535 return strings.Join(attrs, "|") 536 } 537 538 // Combine returns a new action by taking `other` into account. 539 func (bra BlockRequestAction) Combine( 540 other BlockRequestAction) BlockRequestAction { 541 combined := bra | other 542 // If the actions don't agree on stop-if-full, we should remove it 543 // from the combined result. 544 if bra.StopIfFull() != other.StopIfFull() { 545 combined &^= blockRequestStopIfFull 546 } 547 return combined 548 } 549 550 func (bra BlockRequestAction) prefetch() bool { 551 return bra&blockRequestPrefetch > 0 552 } 553 554 // Prefetch returns true if the action indicates the block should 555 // trigger a prefetch. 556 func (bra BlockRequestAction) Prefetch(block data.Block) bool { 557 // When syncing, always prefetch child blocks of an indirect 558 // block, since it makes no sense to sync just part of a 559 // multi-block object. 560 if block.IsIndirect() && bra.Sync() { 561 return true 562 } 563 return bra.prefetch() 564 } 565 566 // PrefetchTracked returns true if this block is being tracked by the 567 // prefetcher. 568 func (bra BlockRequestAction) PrefetchTracked() bool { 569 return bra.prefetch() || bra&blockRequestTrackedInPrefetch > 0 570 } 571 572 // Sync returns true if the action indicates the block should go into 573 // the sync cache. 574 func (bra BlockRequestAction) Sync() bool { 575 return bra&blockRequestSync > 0 && bra&blockRequestNonMasterBranch == 0 576 } 577 578 // DeepSync returns true if the action indicates a deep-syncing of the 579 // block tree rooted at the given block. 580 func (bra BlockRequestAction) DeepSync() bool { 581 // The delayed cache check doesn't affect deep-syncing. Note that 582 // if the mnon-master branch check attribute is set, we want 583 // deep-syncing to fail. 584 return bra.WithoutDelayedCacheCheckAction() == BlockRequestWithDeepSync 585 } 586 587 // DeepPrefetch returns true if the prefetcher should continue 588 // prefetching the children of this block all the way to the leafs of 589 // the tree. 590 func (bra BlockRequestAction) DeepPrefetch() bool { 591 return bra.DeepSync() || bra == BlockRequestPrefetchUntilFull 592 } 593 594 // ChildAction returns the action that should propagate down to any 595 // children of this block. 596 func (bra BlockRequestAction) ChildAction(block data.Block) BlockRequestAction { 597 // When syncing, always prefetch child blocks of an indirect 598 // block, since it makes no sense to sync just part of a 599 // multi-block object. 600 if bra.DeepPrefetch() || (block.IsIndirect() && bra.Sync()) { 601 return bra 602 } 603 // If it's been configured for stop-prefetch-if-full, move to the 604 // stop-if-full action for the child actions. 605 if bra&blockRequestStopPrefetchIfFull > 0 { 606 bra &^= blockRequestStopPrefetchIfFull 607 bra |= blockRequestStopIfFull 608 } 609 return bra &^ (blockRequestPrefetch | blockRequestSync) 610 } 611 612 // SoloAction returns a solo-fetch action based on `bra` (e.g., 613 // preserving the sync bit but nothing else). 614 func (bra BlockRequestAction) SoloAction() BlockRequestAction { 615 return bra & blockRequestSync 616 } 617 618 // AddSync returns a new action that adds syncing in addition to the 619 // original request. For prefetch requests, it returns a deep-sync 620 // request (unlike `Combine`, which just adds the regular sync bit). 621 func (bra BlockRequestAction) AddSync() BlockRequestAction { 622 if bra.prefetch() { 623 return BlockRequestWithDeepSync 624 } 625 // If the prefetch bit is NOT yet set (as when some component 626 // makes a solo request, for example), we should not kick off a 627 // deep sync since the action explicit prohibits any more blocks 628 // being fetched (and doing so will mess up sensitive tests). 629 return bra | blockRequestSync 630 } 631 632 // AddPrefetch returns a new action that adds prefetching in addition 633 // to the original request. For sync requests, it returns a 634 // deep-sync request (unlike `Combine`, which just adds the regular 635 // prefetch bit). 636 func (bra BlockRequestAction) AddPrefetch() BlockRequestAction { 637 if bra.Sync() { 638 return BlockRequestWithDeepSync 639 } 640 641 return bra | blockRequestPrefetch | blockRequestTrackedInPrefetch 642 } 643 644 // AddNonMasterBranch returns a new action that indicates the request 645 // is for a block on a non-master branch. 646 func (bra BlockRequestAction) AddNonMasterBranch() BlockRequestAction { 647 return bra | blockRequestNonMasterBranch 648 } 649 650 // NonMasterBranch returns true if the block is being fetched for a 651 // branch other than the master branch. 652 func (bra BlockRequestAction) NonMasterBranch() bool { 653 return bra&blockRequestNonMasterBranch > 0 654 } 655 656 // CacheType returns the disk block cache type that should be used, 657 // according to the type of action. 658 func (bra BlockRequestAction) CacheType() DiskBlockCacheType { 659 if bra.Sync() { 660 return DiskBlockSyncCache 661 } 662 return DiskBlockAnyCache 663 } 664 665 // StopIfFull returns true if prefetching should stop for good (i.e., 666 // not get rescheduled) when the corresponding disk cache is full. 667 func (bra BlockRequestAction) StopIfFull() bool { 668 return bra&blockRequestStopIfFull > 0 669 } 670 671 // StopPrefetchIfFull returns true if prefetching _after this request_ 672 // should stop for good (i.e., not get rescheduled) when the 673 // corresponding disk cache is full. This request, however, will be 674 // processed even when the cache is full. 675 func (bra BlockRequestAction) StopPrefetchIfFull() bool { 676 return bra&blockRequestStopPrefetchIfFull > 0 677 } 678 679 // AddStopIfFull returns a new action that adds the "stop-if-full" 680 // behavior in addition to the original request. 681 func (bra BlockRequestAction) AddStopIfFull() BlockRequestAction { 682 return bra | blockRequestStopIfFull 683 } 684 685 // AddStopPrefetchIfFull returns a new action that adds the 686 // "stop-prefetch-if-full" behavior in addition to the original request. 687 func (bra BlockRequestAction) AddStopPrefetchIfFull() BlockRequestAction { 688 return bra | blockRequestStopPrefetchIfFull 689 } 690 691 // DelayedCacheCheckAction returns a new action that adds the 692 // delayed-cache-check feature to `bra`. 693 func (bra BlockRequestAction) DelayedCacheCheckAction() BlockRequestAction { 694 return bra | blockRequestDelayCacheCheck 695 } 696 697 // WithoutDelayedCacheCheckAction returns a new action that strips the 698 // delayed-cache-check feature from `bra`. 699 func (bra BlockRequestAction) WithoutDelayedCacheCheckAction() BlockRequestAction { 700 return bra &^ blockRequestDelayCacheCheck 701 } 702 703 // DelayCacheCheck returns true if the disk cache check for a block 704 // request should be delayed until the request is being serviced by a 705 // block worker, in order to improve the performance of the inline 706 // `Request` call. 707 func (bra BlockRequestAction) DelayCacheCheck() bool { 708 return bra&blockRequestDelayCacheCheck > 0 709 } 710 711 // PrefetchProgress tracks the number of bytes fetched for the block 712 // tree rooted at a given block, along with the known total number of 713 // bytes in that tree, and the start time of the prefetch. Note that 714 // the total can change over time as more blocks are downloaded. 715 type PrefetchProgress struct { 716 SubtreeBytesFetched uint64 717 SubtreeBytesTotal uint64 718 Start time.Time 719 } 720 721 // ToProtocolProgress creates a progress suitable of being sent over 722 // the keybase1 protocol to the service. 723 func (p PrefetchProgress) ToProtocolProgress(clock Clock) ( 724 out keybase1.PrefetchProgress) { 725 out.BytesFetched = int64(p.SubtreeBytesFetched) 726 out.BytesTotal = int64(p.SubtreeBytesTotal) 727 out.Start = keybase1.ToTime(p.Start) 728 729 if out.BytesTotal == 0 || out.Start == 0 { 730 return out 731 } 732 733 timeRunning := clock.Now().Sub(p.Start) 734 fracDone := float64(out.BytesFetched) / float64(out.BytesTotal) 735 totalTimeEstimate := time.Duration(float64(timeRunning) / fracDone) 736 endEstimate := p.Start.Add(totalTimeEstimate) 737 out.EndEstimate = keybase1.ToTime(endEstimate) 738 return out 739 } 740 741 // ToProtocolStatus creates a status suitable of being sent over the 742 // keybase1 protocol to the service. It never generates NOT_STARTED 743 // since that doesn't make sense once you already have a prefetch 744 // progress created. 745 func (p PrefetchProgress) ToProtocolStatus() keybase1.PrefetchStatus { 746 if p.SubtreeBytesTotal == p.SubtreeBytesFetched || 747 p.SubtreeBytesTotal == 0 { 748 return keybase1.PrefetchStatus_COMPLETE 749 } 750 return keybase1.PrefetchStatus_IN_PROGRESS 751 } 752 753 type parsedPath struct { 754 tlfType tlf.Type 755 tlfName string 756 rawInTlfPath string 757 rawFullPath userPath 758 } 759 760 func parsePath(path userPath) (parsed *parsedPath, err error) { 761 if !strings.HasPrefix(string(path), "/keybase") { 762 return nil, errors.New("not a KBFS path") 763 } 764 parsed = &parsedPath{tlfType: tlf.Unknown, rawFullPath: path} 765 elems := strings.Split(string(path[1:]), "/") 766 if len(elems) < 2 { 767 return parsed, nil 768 } 769 parsed.tlfType, err = tlf.ParseTlfTypeFromPath(elems[1]) 770 if err != nil { 771 return nil, err 772 } 773 if len(elems) < 3 { 774 return parsed, nil 775 } 776 parsed.tlfName = elems[2] 777 if len(elems) == 3 { 778 parsed.rawInTlfPath = "/" 779 return parsed, nil 780 } 781 parsed.rawInTlfPath = "/" + strings.Join(elems[3:], "/") 782 return parsed, nil 783 } 784 785 func (p *parsedPath) getRootNode(ctx context.Context, config Config) (Node, error) { 786 if p.tlfType == tlf.Unknown || len(p.tlfName) == 0 { 787 return nil, errors.New("path does not have a TLF") 788 } 789 tlfHandle, err := GetHandleFromFolderNameAndType( 790 ctx, config.KBPKI(), config.MDOps(), config, p.tlfName, p.tlfType) 791 if err != nil { 792 return nil, err 793 } 794 branch := data.MasterBranch 795 if tlfHandle.IsLocalConflict() { 796 b, ok := data.MakeConflictBranchName(tlfHandle) 797 if ok { 798 branch = b 799 } 800 } 801 // Get the root node first to initialize the TLF. 802 node, _, err := config.KBFSOps().GetRootNode( 803 ctx, tlfHandle, branch) 804 if err != nil { 805 return nil, err 806 } 807 return node, nil 808 } 809 810 func (p *parsedPath) getFolderBranch(ctx context.Context, config Config) (data.FolderBranch, error) { 811 node, err := p.getRootNode(ctx, config) 812 if err != nil { 813 return data.FolderBranch{}, err 814 } 815 if node == nil { 816 return data.FolderBranch{}, nil 817 } 818 return node.GetFolderBranch(), nil 819 }