github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/data/interfaces.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 data 6 7 import ( 8 "context" 9 10 "github.com/keybase/client/go/kbfs/kbfsblock" 11 "github.com/keybase/client/go/kbfs/libkey" 12 "github.com/keybase/client/go/kbfs/tlf" 13 ) 14 15 // Versioner defines a method for getting the version of some piece of 16 // data. 17 type Versioner interface { 18 // DataVersion returns the data version for this block 19 DataVersion() Ver 20 } 21 22 // Offset is a generic representation of an offset to an indirect 23 // pointer within an indirect Block. 24 type Offset interface { 25 Equals(other Offset) bool 26 Less(other Offset) bool 27 } 28 29 // Block just needs to be (de)serialized using msgpack 30 type Block interface { 31 Versioner 32 // GetEncodedSize returns the encoded size of this block, but only 33 // if it has been previously set; otherwise it returns 0. 34 GetEncodedSize() uint32 35 // SetEncodedSize sets the encoded size of this block, locally 36 // caching it. The encoded size is not serialized. 37 SetEncodedSize(size uint32) 38 // NewEmpty returns a new block of the same type as this block 39 NewEmpty() Block 40 // NewEmptier returns a function that creates a new block of the 41 // same type as this block. 42 NewEmptier() func() Block 43 // Set sets this block to the same value as the passed-in block 44 Set(other Block) 45 // ToCommonBlock retrieves this block as a *CommonBlock. 46 ToCommonBlock() *CommonBlock 47 // IsIndirect indicates whether this block contains indirect pointers. 48 IsIndirect() bool 49 // IsTail returns true if this block doesn't point to any other 50 // blocks, either indirectly or in child directory entries. 51 IsTail() bool 52 // OffsetExceedsData returns true if `off` is greater than the 53 // data contained in a direct block, assuming it starts at 54 // `startOff`. Note that the offset of the next block isn't 55 // relevant; this function should only indicate whether the offset 56 // is greater than what currently could be stored in this block. 57 OffsetExceedsData(startOff, off Offset) bool 58 // BytesCanBeDirtied returns the number of bytes that should be 59 // marked as dirtied if this block is dirtied. 60 BytesCanBeDirtied() int64 61 } 62 63 // BlockWithPtrs defines methods needed for interacting with indirect 64 // pointers. 65 type BlockWithPtrs interface { 66 Block 67 68 // FirstOffset returns the offset of the indirect pointer that 69 // points to the first (left-most) block in a block tree. 70 FirstOffset() Offset 71 // NumIndirectPtrs returns the number of indirect pointers in this 72 // block. The behavior is undefined when called on a non-indirect 73 // block. 74 NumIndirectPtrs() int 75 // IndirectPtr returns the block info and offset for the indirect 76 // pointer at index `i`. The behavior is undefined when called on 77 // a non-indirect block. 78 IndirectPtr(i int) (BlockInfo, Offset) 79 // AppendNewIndirectPtr appends a new indirect pointer at the 80 // given offset. 81 AppendNewIndirectPtr(ptr BlockPointer, off Offset) 82 // ClearIndirectPtrSize clears the encoded size of the indirect 83 // pointer stored at index `i`. 84 ClearIndirectPtrSize(i int) 85 // SetIndirectPtrType set the type of the indirect pointer stored 86 // at index `i`. 87 SetIndirectPtrType(i int, dt BlockDirectType) 88 // SetIndirectPtrOff set the offset of the indirect pointer stored 89 // at index `i`. 90 SetIndirectPtrOff(i int, off Offset) 91 // SetIndirectPtrInfo sets the block info of the indirect pointer 92 // stored at index `i`. 93 SetIndirectPtrInfo(i int, info BlockInfo) 94 // SwapIndirectPtrs swaps the indirect ptr at `i` in this block 95 // with the one at `otherI` in `other`. 96 SwapIndirectPtrs(i int, other BlockWithPtrs, otherI int) 97 } 98 99 // BlockSplitter decides when a file block needs to be split 100 type BlockSplitter interface { 101 // CopyUntilSplit copies data into the block until we reach the 102 // point where we should split, but only if writing to the end of 103 // the last block. If this is writing into the middle of a file, 104 // just copy everything that will fit into the block, and assume 105 // that block boundaries will be fixed later. Return how much was 106 // copied. 107 CopyUntilSplit( 108 block *FileBlock, lastBlock bool, data []byte, off int64) int64 109 110 // CheckSplit, given a block, figures out whether it ends at the 111 // right place. If so, return 0. If not, return either the 112 // offset in the block where it should be split, or -1 if more 113 // bytes from the next block should be appended. 114 CheckSplit(block *FileBlock) int64 115 116 // MaxPtrsPerBlock describes the number of indirect pointers we 117 // can fit into one indirect block. 118 MaxPtrsPerBlock() int 119 120 // ShouldEmbedData decides whether we should keep the data of size 121 // `size` embedded in the MD or not. 122 ShouldEmbedData(size uint64) bool 123 124 // SplitDirIfNeeded splits a direct DirBlock into multiple blocks 125 // if needed. It may modify `block`. If a split isn't needed, it 126 // returns a one-element slice containing `block`. If a split is 127 // needed, it returns a non-nil offset for the new block. 128 SplitDirIfNeeded(block *DirBlock) ([]*DirBlock, *StringOffset) 129 } 130 131 // BlockCacheSimple gets and puts plaintext dir blocks and file blocks into 132 // a cache. These blocks are immutable and identified by their 133 // content hash. 134 type BlockCacheSimple interface { 135 // Get gets the block associated with the given block ID. 136 Get(ptr BlockPointer) (Block, error) 137 // Put stores the final (content-addressable) block associated 138 // with the given block ID. If lifetime is TransientEntry, then it 139 // is assumed that the block exists on the server and the entry 140 // may be evicted from the cache at any time. If lifetime is 141 // PermanentEntry, then it is assumed that the block doesn't exist 142 // on the server and must remain in the cache until explicitly 143 // removed. As an intermediary state, as when a block is being 144 // sent to the server, the block may be put into the cache both 145 // with TransientEntry and PermanentEntry -- these are two 146 // separate entries. This is fine, since the block should be the 147 // same. `hashBehavior` indicates whether the plaintext contents 148 // of transient, direct blocks should be hashed, in order to 149 // identify blocks that can be de-duped. 150 Put(ptr BlockPointer, tlf tlf.ID, block Block, 151 lifetime BlockCacheLifetime, hashBehavior BlockCacheHashBehavior) error 152 } 153 154 // BlockCache specifies the interface of BlockCacheSimple, and also more 155 // advanced and internal methods. 156 type BlockCache interface { 157 BlockCacheSimple 158 // CheckForKnownPtr sees whether this cache has a transient 159 // entry for the given file block, which must be a direct file 160 // block containing data). Returns the full BlockPointer 161 // associated with that ID, including key and data versions. 162 // If no ID is known, return an uninitialized BlockPointer and 163 // a nil error. 164 CheckForKnownPtr( 165 tlf tlf.ID, block *FileBlock, hashBehavior BlockCacheHashBehavior) ( 166 BlockPointer, error) 167 // DeleteTransient removes the transient entry for the given 168 // ID from the cache, as well as any cached IDs so the block 169 // won't be reused. 170 DeleteTransient(id kbfsblock.ID, tlf tlf.ID) error 171 // Delete removes the permanent entry for the non-dirty block 172 // associated with the given block ID from the cache. No 173 // error is returned if no block exists for the given ID. 174 DeletePermanent(id kbfsblock.ID) error 175 // DeleteKnownPtr removes the cached ID for the given file 176 // block. It does not remove the block itself. 177 DeleteKnownPtr(tlf tlf.ID, block *FileBlock) error 178 // GetWithLifetime retrieves a block from the cache, along with 179 // the block's lifetime. 180 GetWithLifetime(ptr BlockPointer) ( 181 block Block, lifetime BlockCacheLifetime, err error) 182 183 // SetCleanBytesCapacity atomically sets clean bytes capacity for block 184 // cache. 185 SetCleanBytesCapacity(capacity uint64) 186 187 // GetCleanBytesCapacity atomically gets clean bytes capacity for block 188 // cache. 189 GetCleanBytesCapacity() (capacity uint64) 190 } 191 192 // IsDirtyProvider defines a method for checking whether a given 193 // pointer is dirty. 194 type IsDirtyProvider interface { 195 // IsDirty states whether or not the block associated with the 196 // given block pointer and branch name is dirty in this cache. 197 IsDirty(tlfID tlf.ID, ptr BlockPointer, branch BranchName) bool 198 } 199 200 // ReadyProvider defines a method for readying a block. 201 type ReadyProvider interface { 202 // Ready turns the given block (which belongs to the TLF with 203 // the given key metadata) into encoded (and encrypted) data, 204 // and calculates its ID and size, so that we can do a bunch 205 // of block puts in parallel for every write. Ready() must 206 // guarantee that plainSize <= readyBlockData.QuotaSize(). 207 Ready(ctx context.Context, kmd libkey.KeyMetadata, block Block) ( 208 id kbfsblock.ID, plainSize int, readyBlockData ReadyBlockData, 209 err error) 210 } 211 212 // BlockPutState is an interface for keeping track of readied blocks 213 // before putting them to the bserver. 214 type BlockPutState interface { 215 AddNewBlock( 216 ctx context.Context, blockPtr BlockPointer, block Block, 217 readyBlockData ReadyBlockData, syncedCb func() error) error 218 SaveOldPtr(ctx context.Context, oldPtr BlockPointer) error 219 Ptrs() []BlockPointer 220 GetBlock(ctx context.Context, blockPtr BlockPointer) (Block, error) 221 } 222 223 // DirtyBlockCacheSimple is a bare-bones interface for a dirty block 224 // cache. 225 type DirtyBlockCacheSimple interface { 226 // Get gets the block associated with the given block ID. Returns 227 // the dirty block for the given ID, if one exists. 228 Get( 229 ctx context.Context, tlfID tlf.ID, ptr BlockPointer, 230 branch BranchName) (Block, error) 231 // Put stores a dirty block currently identified by the 232 // given block pointer and branch name. 233 Put( 234 ctx context.Context, tlfID tlf.ID, ptr BlockPointer, branch BranchName, 235 block Block) error 236 } 237 238 // DirtyPermChan is a channel that gets closed when the holder has 239 // permission to write. We are forced to define it as a type due to a 240 // bug in mockgen that can't handle return values with a chan 241 // struct{}. 242 type DirtyPermChan <-chan struct{} 243 244 // DirtyBlockCache gets and puts plaintext dir blocks and file blocks 245 // into a cache, which have been modified by the application and not 246 // yet committed on the KBFS servers. They are identified by a 247 // (potentially random) ID that may not have any relationship with 248 // their context, along with a Branch in case the same TLF is being 249 // modified via multiple branches. Dirty blocks are never evicted, 250 // they must be deleted explicitly. 251 type DirtyBlockCache interface { 252 IsDirtyProvider 253 DirtyBlockCacheSimple 254 255 // Delete removes the dirty block associated with the given block 256 // pointer and branch from the cache. No error is returned if no 257 // block exists for the given ID. 258 Delete(tlfID tlf.ID, ptr BlockPointer, branch BranchName) error 259 // IsAnyDirty returns whether there are any dirty blocks in the 260 // cache. tlfID may be ignored. 261 IsAnyDirty(tlfID tlf.ID) bool 262 // RequestPermissionToDirty is called whenever a user wants to 263 // write data to a file. The caller provides an estimated number 264 // of bytes that will become dirty -- this is difficult to know 265 // exactly without pre-fetching all the blocks involved, but in 266 // practice we can just use the number of bytes sent in via the 267 // Write. It returns a channel that blocks until the cache is 268 // ready to receive more dirty data, at which point the channel is 269 // closed. The user must call 270 // `UpdateUnsyncedBytes(-estimatedDirtyBytes)` once it has 271 // completed its write and called `UpdateUnsyncedBytes` for all 272 // the exact dirty block sizes. 273 RequestPermissionToDirty(ctx context.Context, tlfID tlf.ID, 274 estimatedDirtyBytes int64) (DirtyPermChan, error) 275 // UpdateUnsyncedBytes is called by a user, who has already been 276 // granted permission to write, with the delta in block sizes that 277 // were dirtied as part of the write. So for example, if a 278 // newly-dirtied block of 20 bytes was extended by 5 bytes, they 279 // should send 25. If on the next write (before any syncs), bytes 280 // 10-15 of that same block were overwritten, they should send 0 281 // over the channel because there were no new bytes. If an 282 // already-dirtied block is truncated, or if previously requested 283 // bytes have now been updated more accurately in previous 284 // requests, newUnsyncedBytes may be negative. wasSyncing should 285 // be true if `BlockSyncStarted` has already been called for this 286 // block. 287 UpdateUnsyncedBytes(tlfID tlf.ID, newUnsyncedBytes int64, wasSyncing bool) 288 // UpdateSyncingBytes is called when a particular block has 289 // started syncing, or with a negative number when a block is no 290 // longer syncing due to an error (and BlockSyncFinished will 291 // never be called). 292 UpdateSyncingBytes(tlfID tlf.ID, size int64) 293 // BlockSyncFinished is called when a particular block has 294 // finished syncing, though the overall sync might not yet be 295 // complete. This lets the cache know it might be able to grant 296 // more permission to writers. 297 BlockSyncFinished(tlfID tlf.ID, size int64) 298 // SyncFinished is called when a complete sync has completed and 299 // its dirty blocks have been removed from the cache. This lets 300 // the cache know it might be able to grant more permission to 301 // writers. 302 SyncFinished(tlfID tlf.ID, size int64) 303 // ShouldForceSync returns true if the sync buffer is full enough 304 // to force all callers to sync their data immediately. 305 ShouldForceSync(tlfID tlf.ID) bool 306 307 // Shutdown frees any resources associated with this instance. It 308 // returns an error if there are any unsynced blocks. 309 Shutdown() error 310 } 311 312 // Obfuscator can transform a given plaintext string into a 313 // securely-obfuscated, but still human-readable, string. 314 type Obfuscator interface { 315 // Obfuscate returns an obfuscated version of `plaintext`. 316 Obfuscate(plaintext string) string 317 }