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