github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/data/data_types.go (about)

     1  // Copyright 2019 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package data
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/keybase/client/go/kbfs/kbfsblock"
    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  	"github.com/keybase/client/go/kbfs/tlfhandle"
    20  	"github.com/keybase/client/go/protocol/keybase1"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  const (
    25  	// MaxBlockSizeBytesDefault is the default maximum block size for KBFS.
    26  	// 512K blocks by default, block changes embedded max == 8K.
    27  	// Block size was chosen somewhat arbitrarily by trying to
    28  	// minimize the overall size of the history written by a user when
    29  	// appending 1KB writes to a file, up to a 1GB total file.  Here
    30  	// is the output of a simple script that approximates that
    31  	// calculation:
    32  	//
    33  	// Total history size for 0065536-byte blocks: 1134341128192 bytes
    34  	// Total history size for 0131072-byte blocks: 618945052672 bytes
    35  	// Total history size for 0262144-byte blocks: 412786622464 bytes
    36  	// Total history size for 0524288-byte blocks: 412786622464 bytes
    37  	// Total history size for 1048576-byte blocks: 618945052672 bytes
    38  	// Total history size for 2097152-byte blocks: 1134341128192 bytes
    39  	// Total history size for 4194304-byte blocks: 2216672886784 bytes
    40  	MaxBlockSizeBytesDefault = 512 << 10
    41  	// MaxNameBytesDefault is the max supported size of a directory
    42  	// entry name.
    43  	MaxNameBytesDefault = 255
    44  	// BackgroundTaskTimeout is the timeout for any background task.
    45  	BackgroundTaskTimeout = 1 * time.Minute
    46  )
    47  
    48  // Ver is the type of a version for marshalled KBFS data structures.
    49  //
    50  // 1) Ver is a per-block attribute, not per-file. This means that,
    51  // in theory, an indirect block with DataVer n may point to blocks
    52  // with Vers less than, equal to, or greater than n. However, for
    53  // now, it's guaranteed that an indirect block will never point to
    54  // blocks with greater versions than itself. (See #3 for details.)
    55  //
    56  // 2) Ver is an external attribute of a block, meaning that it's
    57  // not stored as part of the block, but computed by the creator (or
    58  // anyone with the latest kbfs client), and stored only in pointers to
    59  // the block.
    60  //
    61  // 2.5) A file (or, in the future a dir) can in theory have any
    62  // arbitrary tree structure of blocks. However, we only write files
    63  // such that all paths to leaves have the same depth.
    64  //
    65  // Currently, in addition to 2.5, we have the following constraints on block
    66  // tree structures:
    67  // a) Direct blocks are always v1.
    68  // b) Indirect blocks of depth 2 (meaning one indirect block pointing
    69  // to all direct blocks) can be v1 (if it has no holes) or v2 (if it has
    70  // holes). However, all its indirect pointers will have Ver
    71  // 1, by a).
    72  // c) Indirect blocks of depth 3 must be v3 and must have at least one
    73  // indirect pointer with an indirect DirectType [although if it holds
    74  // for one, it should hold for all], although its indirect pointers
    75  // may have any combination of Ver 1 or 2, by b).
    76  // d) Indirect blocks of dept k > 3 must be v3 and must have at least
    77  // one indirect pointer with an indirect DirectType [although if it
    78  // holds for one, it should hold for all], and all of its indirect
    79  // pointers must have Ver 3, by c).
    80  type Ver int
    81  
    82  const (
    83  	// FirstValidVer is the first value that is considered a
    84  	// valid data version. Note that the nil value is not
    85  	// considered valid.
    86  	FirstValidVer Ver = 1
    87  	// ChildHolesVer is the data version for any indirect block
    88  	// containing a set of pointers with holes.
    89  	ChildHolesVer Ver = 2
    90  	// AtLeastTwoLevelsOfChildrenVer is the data version for
    91  	// blocks that have multiple levels of indirection below them
    92  	// (i.e., indirect blocks that point to other indirect blocks).
    93  	AtLeastTwoLevelsOfChildrenVer Ver = 3
    94  	// IndirectDirsVer is the data version for a directory block
    95  	// that contains indirect pointers.
    96  	IndirectDirsVer Ver = 4
    97  )
    98  
    99  // BlockReqType indicates whether an operation makes block
   100  // modifications or not
   101  type BlockReqType int
   102  
   103  const (
   104  	// BlockRead indicates a block read request.
   105  	BlockRead BlockReqType = iota
   106  	// BlockWrite indicates a block write request.
   107  	BlockWrite
   108  	// BlockReadParallel indicates a block read request that is
   109  	// happening from a different goroutine than the blockLock rlock
   110  	// holder, using the same lState.
   111  	BlockReadParallel
   112  	// BlockLookup indicates a lookup for a block for the purposes of
   113  	// creating a new node in the node cache for it; avoid any unlocks
   114  	// as part of the lookup process.
   115  	BlockLookup
   116  )
   117  
   118  // BlockDirectType indicates to what kind of block (direct or
   119  // indirect) a BlockPointer points.
   120  type BlockDirectType int
   121  
   122  const (
   123  	// UnknownDirectType indicates an old block that was written
   124  	// before we started labeling pointers.
   125  	UnknownDirectType BlockDirectType = 0
   126  	// DirectBlock indicates the pointed-to block has no indirect
   127  	// pointers.
   128  	DirectBlock BlockDirectType = 1
   129  	// IndirectBlock indicates the pointed-to block has indirect
   130  	// pointers.
   131  	IndirectBlock BlockDirectType = 2
   132  )
   133  
   134  func (bdt BlockDirectType) String() string {
   135  	switch bdt {
   136  	case UnknownDirectType:
   137  		return "unknown"
   138  	case DirectBlock:
   139  		return "direct"
   140  	case IndirectBlock:
   141  		return "indirect"
   142  	}
   143  	return fmt.Sprintf("<unknown blockDirectType %d>", bdt)
   144  }
   145  
   146  // BlockDirectTypeFromString returns a direct block type, given the string.
   147  func BlockDirectTypeFromString(s string) BlockDirectType {
   148  	switch s {
   149  	case "direct":
   150  		return DirectBlock
   151  	case "indirect":
   152  		return IndirectBlock
   153  	default:
   154  		return UnknownDirectType
   155  	}
   156  }
   157  
   158  // BlockRef is a block ID/ref nonce pair, which defines a unique
   159  // reference to a block.
   160  type BlockRef struct {
   161  	ID       kbfsblock.ID
   162  	RefNonce kbfsblock.RefNonce
   163  }
   164  
   165  // IsValid returns true exactly when ID.IsValid() does.
   166  func (r BlockRef) IsValid() bool {
   167  	return r.ID.IsValid()
   168  }
   169  
   170  func (r BlockRef) String() string {
   171  	s := fmt.Sprintf("BlockRef{id: %s", r.ID)
   172  	if r.RefNonce != kbfsblock.ZeroRefNonce {
   173  		s += fmt.Sprintf(", refNonce: %s", r.RefNonce)
   174  	}
   175  	s += "}"
   176  	return s
   177  }
   178  
   179  // BlockPointer contains the identifying information for a block in KBFS.
   180  //
   181  // NOTE: Don't add or modify anything in this struct without
   182  // considering how old clients will handle them.
   183  type BlockPointer struct {
   184  	ID         kbfsblock.ID    `codec:"i"`
   185  	KeyGen     kbfsmd.KeyGen   `codec:"k"`           // if valid, which generation of the TLF{Writer,Reader}KeyBundle to use.
   186  	DataVer    Ver             `codec:"d"`           // if valid, which version of the KBFS data structures is pointed to
   187  	DirectType BlockDirectType `codec:"t,omitempty"` // the type (direct, indirect, or unknown [if omitted]) of the pointed-to block
   188  	kbfsblock.Context
   189  }
   190  
   191  // ZeroPtr represents an empty BlockPointer.
   192  var ZeroPtr BlockPointer
   193  
   194  // IsValid returns whether the block pointer is valid. A zero block
   195  // pointer is considered invalid.
   196  func (p BlockPointer) IsValid() bool {
   197  	return p.ID.IsValid()
   198  
   199  	// TODO: Should also check KeyGen, Ver, and Creator. (A
   200  	// bunch of tests use invalid values for one of these.)
   201  }
   202  
   203  func (p BlockPointer) String() string {
   204  	if p == (BlockPointer{}) {
   205  		return "BlockPointer{}"
   206  	}
   207  	return fmt.Sprintf("BlockPointer{ID: %s, KeyGen: %d, DataVer: %d, "+
   208  		"Context: %s, DirectType: %s}",
   209  		p.ID, p.KeyGen, p.DataVer, p.Context, p.DirectType)
   210  }
   211  
   212  // IsInitialized returns whether or not this BlockPointer has non-nil data.
   213  func (p BlockPointer) IsInitialized() bool {
   214  	return p.ID != kbfsblock.ID{}
   215  }
   216  
   217  // Ref returns the BlockRef equivalent of this pointer.
   218  func (p BlockPointer) Ref() BlockRef {
   219  	return BlockRef{
   220  		ID:       p.ID,
   221  		RefNonce: p.RefNonce,
   222  	}
   223  }
   224  
   225  // BlockInfo contains all information about a block in KBFS and its
   226  // contents.
   227  //
   228  // NOTE: Don't add or modify anything in this struct without
   229  // considering how old clients will handle them.
   230  type BlockInfo struct {
   231  	BlockPointer
   232  	// When non-zero, the size of the encoded (and possibly
   233  	// encrypted) data contained in the block. When non-zero,
   234  	// always at least the size of the plaintext data contained in
   235  	// the block.
   236  	EncodedSize uint32 `codec:"e"`
   237  }
   238  
   239  func (bi BlockInfo) String() string {
   240  	if bi == (BlockInfo{}) {
   241  		return "BlockInfo{}"
   242  	}
   243  	return fmt.Sprintf("BlockInfo{BlockPointer: %s, EncodedSize: %d}",
   244  		bi.BlockPointer, bi.EncodedSize)
   245  }
   246  
   247  // BPSize is the estimated size of a block pointer in bytes.
   248  var BPSize = uint64(reflect.TypeOf(BlockPointer{}).Size())
   249  
   250  // ReadyBlockData is a block that has been encoded (and encrypted).
   251  type ReadyBlockData struct {
   252  	// These fields should not be used outside of putBlockToServer.
   253  	Buf        []byte
   254  	ServerHalf kbfscrypto.BlockCryptKeyServerHalf
   255  }
   256  
   257  // GetEncodedSize returns the size of the encoded (and encrypted)
   258  // block data.
   259  func (r ReadyBlockData) GetEncodedSize() int {
   260  	return len(r.Buf)
   261  }
   262  
   263  // EntryInfo is the (non-block-related) info a directory knows about
   264  // its child.
   265  //
   266  // NOTE: Don't add or modify anything in this struct without
   267  // considering how old clients will handle them (since this is
   268  // embedded in DirEntry).
   269  type EntryInfo struct {
   270  	Type    EntryType
   271  	Size    uint64
   272  	SymPath string `codec:",omitempty"` // must be within the same root dir
   273  	// Mtime is in unix nanoseconds
   274  	Mtime int64
   275  	// Ctime is in unix nanoseconds
   276  	Ctime int64
   277  	// If this is a team TLF, we want to track the last writer of an
   278  	// entry, since in the block, only the team ID will be tracked.
   279  	TeamWriter keybase1.UID `codec:"tw,omitempty"`
   280  	// Tracks a skiplist of the previous revisions for this entry.
   281  	PrevRevisions PrevRevisions `codec:"pr,omitempty"`
   282  }
   283  
   284  func init() {
   285  	if reflect.ValueOf(EntryInfo{}).NumField() != 7 {
   286  		panic(errors.New(
   287  			"Unexpected number of fields in EntryInfo; " +
   288  				"please update EntryInfo.Eq() for your " +
   289  				"new or removed field"))
   290  	}
   291  }
   292  
   293  // EntryInfoFromFileInfo converts an `os.FileInfo` into an
   294  // `EntryInfo`, to the best of our ability to do so.  The caller is
   295  // responsible for filling in `EntryInfo.SymPath`, if needed.
   296  func EntryInfoFromFileInfo(fi os.FileInfo) EntryInfo {
   297  	t := File
   298  	switch {
   299  	case fi.IsDir():
   300  		t = Dir
   301  	case fi.Mode()&os.ModeSymlink != 0:
   302  		t = Sym
   303  	case fi.Mode()&0100 != 0:
   304  		t = Exec
   305  	}
   306  	mtime := fi.ModTime().UnixNano()
   307  	return EntryInfo{
   308  		Type:  t,
   309  		Size:  uint64(fi.Size()), // TODO: deal with negatives?
   310  		Mtime: mtime,
   311  		Ctime: mtime,
   312  		// Leave TeamWriter and PrevRevisions empty
   313  	}
   314  }
   315  
   316  // Eq returns true if `other` is equal to `ei`.
   317  func (ei EntryInfo) Eq(other EntryInfo) bool {
   318  	eq := ei.Type == other.Type &&
   319  		ei.Size == other.Size &&
   320  		ei.SymPath == other.SymPath &&
   321  		ei.Mtime == other.Mtime &&
   322  		ei.Ctime == other.Ctime &&
   323  		ei.TeamWriter == other.TeamWriter &&
   324  		len(ei.PrevRevisions) == len(other.PrevRevisions)
   325  	if !eq {
   326  		return false
   327  	}
   328  	for i, pr := range ei.PrevRevisions {
   329  		otherPR := other.PrevRevisions[i]
   330  		if pr.Revision != otherPR.Revision || pr.Count != otherPR.Count {
   331  			return false
   332  		}
   333  	}
   334  	return true
   335  }
   336  
   337  // EntryType is the type of a directory entry.
   338  type EntryType int
   339  
   340  const (
   341  	// File is a regular file.
   342  	File EntryType = iota
   343  	// Exec is an executable file.
   344  	Exec
   345  	// Dir is a directory.
   346  	Dir
   347  	// Sym is a symbolic link.
   348  	Sym
   349  
   350  	// RealDir can be used to indicate a real directory entry should
   351  	// be used, usually with a provided BlockPointer.
   352  	RealDir EntryType = 0xfffd
   353  	// FakeFile can be used to indicate a faked-out entry for a file,
   354  	// that will be specially processed by folderBranchOps.
   355  	FakeFile EntryType = 0xfffe
   356  	// FakeDir can be used to indicate a faked-out entry for a directory,
   357  	// that will be specially processed by folderBranchOps.
   358  	FakeDir EntryType = 0xffff
   359  )
   360  
   361  // String implements the fmt.Stringer interface for EntryType
   362  func (et EntryType) String() string {
   363  	switch et {
   364  	case File:
   365  		return "FILE"
   366  	case Exec:
   367  		return "EXEC"
   368  	case Dir:
   369  		return "DIR"
   370  	case Sym:
   371  		return "SYM"
   372  	}
   373  	return "<invalid EntryType>"
   374  }
   375  
   376  // IsFile returns whether or not this entry points to a file.
   377  func (et EntryType) IsFile() bool {
   378  	return et == File || et == Exec
   379  }
   380  
   381  // BranchName is the name given to a KBFS branch, for a particular
   382  // top-level folder.  Currently, the notion of a "branch" is
   383  // client-side only, and can be used to specify which root to use for
   384  // a top-level folder.  (For example, viewing a historical archive
   385  // could use a different branch name.)
   386  type BranchName string
   387  
   388  const (
   389  	// MasterBranch represents the mainline branch for a top-level
   390  	// folder.  Set to the empty string so that the default will be
   391  	// the master branch.
   392  	MasterBranch BranchName = ""
   393  
   394  	branchRevPrefix           = "rev="
   395  	branchLocalConflictPrefix = "localConflict="
   396  )
   397  
   398  // MakeRevBranchName returns a branch name specifying an archive
   399  // branch pinned to the given revision number.
   400  func MakeRevBranchName(rev kbfsmd.Revision) BranchName {
   401  	return BranchName(branchRevPrefix + strconv.FormatInt(int64(rev), 10))
   402  }
   403  
   404  // MakeConflictBranchNameFromExtension returns a branch name
   405  // specifying a conflict date, if possible.
   406  func MakeConflictBranchNameFromExtension(
   407  	ext *tlf.HandleExtension) BranchName {
   408  	return BranchName(branchLocalConflictPrefix + ext.String())
   409  }
   410  
   411  // MakeConflictBranchName returns a branch name specifying a conflict
   412  // date, if possible.
   413  func MakeConflictBranchName(h *tlfhandle.Handle) (BranchName, bool) {
   414  	if !h.IsLocalConflict() {
   415  		return "", false
   416  	}
   417  
   418  	return MakeConflictBranchNameFromExtension(h.ConflictInfo()), true
   419  }
   420  
   421  // IsArchived returns true if the branch specifies an archived revision.
   422  func (bn BranchName) IsArchived() bool {
   423  	return strings.HasPrefix(string(bn), branchRevPrefix)
   424  }
   425  
   426  // IsLocalConflict returns true if the branch specifies a local conflict branch.
   427  func (bn BranchName) IsLocalConflict() bool {
   428  	return strings.HasPrefix(string(bn), branchLocalConflictPrefix)
   429  }
   430  
   431  // RevisionIfSpecified returns a valid revision number and true if
   432  // `bn` is a revision branch.
   433  func (bn BranchName) RevisionIfSpecified() (kbfsmd.Revision, bool) {
   434  	if !bn.IsArchived() {
   435  		return kbfsmd.RevisionUninitialized, false
   436  	}
   437  
   438  	i, err := strconv.ParseInt(string(bn[len(branchRevPrefix):]), 10, 64)
   439  	if err != nil {
   440  		return kbfsmd.RevisionUninitialized, false
   441  	}
   442  
   443  	return kbfsmd.Revision(i), true
   444  }
   445  
   446  // FolderBranch represents a unique pair of top-level folder and a
   447  // branch of that folder.
   448  type FolderBranch struct {
   449  	Tlf    tlf.ID
   450  	Branch BranchName // master branch, by default
   451  }
   452  
   453  func (fb FolderBranch) String() string {
   454  	s := fb.Tlf.String()
   455  	if len(fb.Branch) > 0 {
   456  		s += fmt.Sprintf("(branch=%s)", fb.Branch)
   457  	}
   458  	return s
   459  }
   460  
   461  // BlockCacheLifetime denotes the lifetime of an entry in BlockCache.
   462  type BlockCacheLifetime int
   463  
   464  func (l BlockCacheLifetime) String() string {
   465  	switch l {
   466  	case NoCacheEntry:
   467  		return "NoCacheEntry"
   468  	case TransientEntry:
   469  		return "TransientEntry"
   470  	case PermanentEntry:
   471  		return "PermanentEntry"
   472  	}
   473  	return "Unknown"
   474  }
   475  
   476  const (
   477  	// NoCacheEntry means that the entry will not be cached.
   478  	NoCacheEntry BlockCacheLifetime = iota
   479  	// TransientEntry means that the cache entry may be evicted at
   480  	// any time.
   481  	TransientEntry
   482  	// PermanentEntry means that the cache entry must remain until
   483  	// explicitly removed from the cache.
   484  	PermanentEntry
   485  )
   486  
   487  // BlockCacheHashBehavior denotes whether the cache should hash the
   488  // plaintext of a new block or not.
   489  type BlockCacheHashBehavior int
   490  
   491  const (
   492  	// SkipCacheHash means that the plaintext of a block should not be hashed.
   493  	SkipCacheHash BlockCacheHashBehavior = iota
   494  	// DoCacheHash means that the plaintext of a block should be hashed.
   495  	DoCacheHash
   496  )