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 5package libkbfs 6 7import ( 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. 28var disallowedPrefixes = [...]string{".kbfs"} 29 30// EncryptedTLFCryptKeyClientAndEphemeral has what's needed to 31// request a client half decryption. 32type 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 41const ( 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. 58type 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. 79func (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. 91func (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. 100func (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. 109func (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. 118func (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. 127func (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 140type Excl bool 141 142const ( 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 150func (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. 162type 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. 170type 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. 178type 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. 189type 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. 197type 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. 206type ErrorModeType int 207 208const ( 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. 216type 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. 227type FavoritesOp int 228 229const ( 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. 243type RekeyResult struct { 244 DidRekey bool 245 NeedsPaperKey bool 246} 247 248// InitModeType indicates how KBFS should configure itself at runtime. 249type InitModeType int 250 251const ( 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 279func (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. 301type PrefetchStatus int 302 303// ErrUnrecognizedPrefetchStatus is returned when trying to unmarshal a 304// prefetch status from JSON if the prefetch status is unrecognized. 305var ErrUnrecognizedPrefetchStatus = errors.New( 306 "Unrecognized PrefetchStatus value") 307 308const ( 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 318func (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. 332func (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 346func (s PrefetchStatus) MarshalJSON() ([]byte, error) { 347 return json.Marshal(s.String()) 348} 349 350// UnmarshalJSON converts a PrefetchStatus from JSON 351func (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. 371func (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. 382func 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. 397type 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. 405type 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 411type 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 419func (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 429func 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. 441type 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. 449type 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. 454type BlockRequestAction int 455 456const ( 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 503func (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. 539func (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 550func (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. 556func (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. 568func (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. 574func (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. 580func (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. 590func (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. 596func (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). 614func (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). 621func (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). 636func (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. 646func (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. 652func (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. 658func (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. 667func (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. 675func (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. 681func (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. 687func (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`. 693func (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`. 699func (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. 707func (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. 715type 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. 723func (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. 745func (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 753type parsedPath struct { 754 tlfType tlf.Type 755 tlfName string 756 rawInTlfPath string 757 rawFullPath userPath 758} 759 760func 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 785func (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 810func (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} 820