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  }