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 )