github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/bserver_memory.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 "errors" 9 "fmt" 10 "math" 11 "sync" 12 13 "github.com/keybase/client/go/kbfs/kbfsblock" 14 "github.com/keybase/client/go/kbfs/kbfscrypto" 15 "github.com/keybase/client/go/kbfs/tlf" 16 "github.com/keybase/client/go/logger" 17 "github.com/keybase/client/go/protocol/keybase1" 18 "golang.org/x/net/context" 19 ) 20 21 type blockMemEntry struct { 22 tlfID tlf.ID 23 blockData []byte 24 keyServerHalf kbfscrypto.BlockCryptKeyServerHalf 25 refs blockRefMap 26 } 27 28 // BlockServerMemory implements the BlockServer interface by just 29 // storing blocks in memory. 30 type BlockServerMemory struct { 31 log logger.Logger 32 33 lock sync.RWMutex 34 // m is nil after Shutdown() is called. 35 m map[kbfsblock.ID]blockMemEntry 36 } 37 38 var _ blockServerLocal = (*BlockServerMemory)(nil) 39 40 // NewBlockServerMemory constructs a new BlockServerMemory that stores 41 // its data in memory. 42 func NewBlockServerMemory(log logger.Logger) *BlockServerMemory { 43 return &BlockServerMemory{ 44 log, sync.RWMutex{}, make(map[kbfsblock.ID]blockMemEntry), 45 } 46 } 47 48 var errBlockServerMemoryShutdown = errors.New("BlockServerMemory is shutdown") 49 50 // FastForwardBackoff implements the BlockServer interface. 51 func (b *BlockServerMemory) FastForwardBackoff() {} 52 53 // Get implements the BlockServer interface for BlockServerMemory. 54 func (b *BlockServerMemory) Get( 55 ctx context.Context, tlfID tlf.ID, id kbfsblock.ID, 56 context kbfsblock.Context, _ DiskBlockCacheType) ( 57 data []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf, err error) { 58 if err := checkContext(ctx); err != nil { 59 return nil, kbfscrypto.BlockCryptKeyServerHalf{}, err 60 } 61 62 defer func() { 63 err = translateToBlockServerError(err) 64 }() 65 b.log.CDebugf(ctx, "BlockServerMemory.Get id=%s tlfID=%s context=%s", 66 id, tlfID, context) 67 b.lock.RLock() 68 defer b.lock.RUnlock() 69 70 if b.m == nil { 71 return nil, kbfscrypto.BlockCryptKeyServerHalf{}, 72 errBlockServerMemoryShutdown 73 } 74 75 entry, ok := b.m[id] 76 if !ok { 77 return nil, kbfscrypto.BlockCryptKeyServerHalf{}, 78 kbfsblock.ServerErrorBlockNonExistent{ 79 Msg: fmt.Sprintf("Block ID %s does not exist.", id)} 80 } 81 82 if entry.tlfID != tlfID { 83 return nil, kbfscrypto.BlockCryptKeyServerHalf{}, 84 fmt.Errorf("TLF ID mismatch: expected %s, got %s", 85 entry.tlfID, tlfID) 86 } 87 88 exists, _, err := entry.refs.checkExists(context) 89 if err != nil { 90 return nil, kbfscrypto.BlockCryptKeyServerHalf{}, err 91 } 92 if !exists { 93 return nil, kbfscrypto.BlockCryptKeyServerHalf{}, 94 blockNonExistentError{id} 95 } 96 97 return entry.blockData, entry.keyServerHalf, nil 98 } 99 100 // GetEncodedSizes implements the BlockServer interface for 101 // BlockServerMemory. 102 func (b *BlockServerMemory) GetEncodedSizes( 103 ctx context.Context, tlfID tlf.ID, ids []kbfsblock.ID, 104 contexts []kbfsblock.Context) ( 105 sizes []uint32, statuses []keybase1.BlockStatus, err error) { 106 if err := checkContext(ctx); err != nil { 107 return nil, nil, err 108 } 109 110 defer func() { 111 err = translateToBlockServerError(err) 112 }() 113 b.log.CDebugf(ctx, 114 "BlockServerMemory.GetEncodedSizes ids=%s tlfID=%s contexts=%s", 115 ids, tlfID, contexts) 116 b.lock.RLock() 117 defer b.lock.RUnlock() 118 119 if b.m == nil { 120 return nil, nil, errBlockServerMemoryShutdown 121 } 122 123 sizes = make([]uint32, len(ids)) 124 statuses = make([]keybase1.BlockStatus, len(ids)) 125 for i, id := range ids { 126 entry, ok := b.m[id] 127 if !ok { 128 sizes[i] = 0 129 statuses[i] = keybase1.BlockStatus_UNKNOWN 130 continue 131 } 132 133 if entry.tlfID != tlfID { 134 return nil, nil, fmt.Errorf("TLF ID mismatch: expected %s, got %s", 135 entry.tlfID, tlfID) 136 } 137 138 context := contexts[i] 139 exists, refStatus, err := entry.refs.checkExists(context) 140 if err != nil { 141 return nil, nil, err 142 } 143 if !exists { 144 sizes[i] = 0 145 statuses[i] = keybase1.BlockStatus_UNKNOWN 146 continue 147 } 148 149 sizes[i] = uint32(len(entry.blockData)) 150 statuses[i] = refStatus.toBlockStatus() 151 } 152 153 return sizes, statuses, nil 154 } 155 156 func validateBlockPut(checkNonzeroRef bool, id kbfsblock.ID, context kbfsblock.Context, 157 buf []byte) error { 158 var emptyID keybase1.UserOrTeamID 159 if context.GetCreator() == emptyID { 160 return fmt.Errorf("Can't Put() a block %v with an empty UID", id) 161 } 162 163 if context.GetCreator() != context.GetWriter() { 164 return fmt.Errorf("Can't Put() a block with creator=%s != writer=%s", 165 context.GetCreator(), context.GetWriter()) 166 } 167 168 if checkNonzeroRef && context.GetRefNonce() != kbfsblock.ZeroRefNonce { 169 return errors.New("can't Put() a block with a non-zero refnonce") 170 } 171 172 return verifyLocalBlockIDMaybe(buf, id) 173 } 174 175 // doPut consolidates the put logic for implementing both the Put and PutAgain interface. 176 func (b *BlockServerMemory) doPut(isRegularPut bool, tlfID tlf.ID, id kbfsblock.ID, context kbfsblock.Context, 177 buf []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf) (err error) { 178 defer func() { 179 err = translateToBlockServerError(err) 180 }() 181 err = validateBlockPut(isRegularPut, id, context, buf) 182 if err != nil { 183 return err 184 } 185 186 b.lock.Lock() 187 defer b.lock.Unlock() 188 189 if b.m == nil { 190 return errBlockServerMemoryShutdown 191 } 192 193 var refs blockRefMap 194 if entry, ok := b.m[id]; ok { 195 // If the entry already exists, everything should be 196 // the same, except for possibly additional 197 // references. 198 199 if entry.tlfID != tlfID { 200 return fmt.Errorf( 201 "TLF ID mismatch: expected %s, got %s", 202 entry.tlfID, tlfID) 203 } 204 205 // We checked that buf hashes to id, so no need to 206 // check that it's equal to entry.data (since that was 207 // presumably already checked previously). 208 209 if isRegularPut && entry.keyServerHalf != serverHalf { 210 return fmt.Errorf( 211 "key server half mismatch: expected %s, got %s", 212 entry.keyServerHalf, serverHalf) 213 } 214 215 refs = entry.refs 216 } else { 217 data := make([]byte, len(buf)) 218 copy(data, buf) 219 refs = make(blockRefMap) 220 b.m[id] = blockMemEntry{ 221 tlfID: tlfID, 222 blockData: data, 223 keyServerHalf: serverHalf, 224 refs: refs, 225 } 226 } 227 228 return refs.put(context, liveBlockRef, "") 229 } 230 231 // Put implements the BlockServer interface for BlockServerMemory. 232 func (b *BlockServerMemory) Put( 233 ctx context.Context, tlfID tlf.ID, id kbfsblock.ID, 234 context kbfsblock.Context, buf []byte, 235 serverHalf kbfscrypto.BlockCryptKeyServerHalf, 236 _ DiskBlockCacheType) (err error) { 237 if err := checkContext(ctx); err != nil { 238 return err 239 } 240 b.log.CDebugf(ctx, "BlockServerMemory.Put id=%s tlfID=%s context=%s "+ 241 "size=%d", id, tlfID, context, len(buf)) 242 243 return b.doPut(true, tlfID, id, context, buf, serverHalf) 244 } 245 246 // PutAgain implements the BlockServer interface for BlockServerMemory. 247 func (b *BlockServerMemory) PutAgain( 248 ctx context.Context, tlfID tlf.ID, id kbfsblock.ID, 249 context kbfsblock.Context, buf []byte, 250 serverHalf kbfscrypto.BlockCryptKeyServerHalf, 251 _ DiskBlockCacheType) (err error) { 252 if err := checkContext(ctx); err != nil { 253 return err 254 } 255 b.log.CDebugf(ctx, "BlockServerMemory.PutAgain id=%s tlfID=%s context=%s "+ 256 "size=%d", id, tlfID, context, len(buf)) 257 258 return b.doPut(false, tlfID, id, context, buf, serverHalf) 259 } 260 261 // AddBlockReference implements the BlockServer interface for BlockServerMemory. 262 func (b *BlockServerMemory) AddBlockReference(ctx context.Context, tlfID tlf.ID, 263 id kbfsblock.ID, context kbfsblock.Context) (err error) { 264 if err := checkContext(ctx); err != nil { 265 return err 266 } 267 268 defer func() { 269 err = translateToBlockServerError(err) 270 }() 271 b.log.CDebugf(ctx, "BlockServerMemory.AddBlockReference id=%s "+ 272 "tlfID=%s context=%s", id, tlfID, context) 273 274 b.lock.Lock() 275 defer b.lock.Unlock() 276 277 if b.m == nil { 278 return errBlockServerMemoryShutdown 279 } 280 281 entry, ok := b.m[id] 282 if !ok { 283 return kbfsblock.ServerErrorBlockNonExistent{ 284 Msg: fmt.Sprintf("Block ID %s doesn't "+ 285 "exist and cannot be referenced.", id)} 286 } 287 288 if entry.tlfID != tlfID { 289 return fmt.Errorf("TLF ID mismatch: expected %s, got %s", 290 entry.tlfID, tlfID) 291 } 292 293 return entry.refs.put(context, liveBlockRef, "") 294 } 295 296 func (b *BlockServerMemory) removeBlockReference( 297 tlfID tlf.ID, id kbfsblock.ID, contexts []kbfsblock.Context) ( 298 int, error) { 299 b.lock.Lock() 300 defer b.lock.Unlock() 301 302 if b.m == nil { 303 return 0, errBlockServerMemoryShutdown 304 } 305 306 entry, ok := b.m[id] 307 if !ok { 308 // This block is already gone; no error. 309 return 0, nil 310 } 311 312 if entry.tlfID != tlfID { 313 return 0, fmt.Errorf("TLF ID mismatch: expected %s, got %s", 314 entry.tlfID, tlfID) 315 } 316 317 for _, context := range contexts { 318 err := entry.refs.remove(context, "") 319 if err != nil { 320 return 0, err 321 } 322 } 323 count := len(entry.refs) 324 if count == 0 { 325 delete(b.m, id) 326 } 327 return count, nil 328 } 329 330 // RemoveBlockReferences implements the BlockServer interface for 331 // BlockServerMemory. 332 func (b *BlockServerMemory) RemoveBlockReferences(ctx context.Context, 333 tlfID tlf.ID, contexts kbfsblock.ContextMap) ( 334 liveCounts map[kbfsblock.ID]int, err error) { 335 if err := checkContext(ctx); err != nil { 336 return nil, err 337 } 338 339 defer func() { 340 err = translateToBlockServerError(err) 341 }() 342 b.log.CDebugf(ctx, "BlockServerMemory.RemoveBlockReference "+ 343 "tlfID=%s contexts=%v", tlfID, contexts) 344 liveCounts = make(map[kbfsblock.ID]int) 345 for id, idContexts := range contexts { 346 count, err := b.removeBlockReference(tlfID, id, idContexts) 347 if err != nil { 348 return nil, err 349 } 350 liveCounts[id] = count 351 } 352 return liveCounts, nil 353 } 354 355 func (b *BlockServerMemory) archiveBlockReference( 356 tlfID tlf.ID, id kbfsblock.ID, context kbfsblock.Context) error { 357 b.lock.Lock() 358 defer b.lock.Unlock() 359 360 if b.m == nil { 361 return errBlockServerMemoryShutdown 362 } 363 364 entry, ok := b.m[id] 365 if !ok { 366 return kbfsblock.ServerErrorBlockNonExistent{ 367 Msg: fmt.Sprintf("Block ID %s doesn't "+ 368 "exist and cannot be archived.", id)} 369 } 370 371 if entry.tlfID != tlfID { 372 return fmt.Errorf("TLF ID mismatch: expected %s, got %s", 373 entry.tlfID, tlfID) 374 } 375 376 exists, _, err := entry.refs.checkExists(context) 377 if err != nil { 378 return err 379 } 380 if !exists { 381 return kbfsblock.ServerErrorBlockNonExistent{ 382 Msg: fmt.Sprintf("Block ID %s (ref %s) "+ 383 "doesn't exist and cannot be archived.", 384 id, context.GetRefNonce())} 385 } 386 387 return entry.refs.put(context, archivedBlockRef, "") 388 } 389 390 // ArchiveBlockReferences implements the BlockServer interface for 391 // BlockServerMemory. 392 func (b *BlockServerMemory) ArchiveBlockReferences(ctx context.Context, 393 tlfID tlf.ID, contexts kbfsblock.ContextMap) (err error) { 394 if err := checkContext(ctx); err != nil { 395 return err 396 } 397 398 defer func() { 399 err = translateToBlockServerError(err) 400 }() 401 b.log.CDebugf(ctx, "BlockServerMemory.ArchiveBlockReferences "+ 402 "tlfID=%s contexts=%v", tlfID, contexts) 403 404 for id, idContexts := range contexts { 405 for _, context := range idContexts { 406 err := b.archiveBlockReference(tlfID, id, context) 407 if err != nil { 408 return err 409 } 410 } 411 } 412 413 return nil 414 } 415 416 // GetLiveBlockReferences implements the BlockServer interface for 417 // BlockServerMemory. 418 func (b *BlockServerMemory) GetLiveBlockReferences( 419 ctx context.Context, tlfID tlf.ID, contexts kbfsblock.ContextMap) ( 420 liveCounts map[kbfsblock.ID]int, err error) { 421 if err := checkContext(ctx); err != nil { 422 return nil, err 423 } 424 425 defer func() { 426 err = translateToBlockServerError(err) 427 }() 428 b.log.CDebugf(ctx, "BlockServerMemory.GetLiveBlockReferences "+ 429 "tlfID=%s contexts=%v", tlfID, contexts) 430 431 b.lock.Lock() 432 defer b.lock.Unlock() 433 434 if b.m == nil { 435 return nil, errBlockServerMemoryShutdown 436 } 437 438 liveCounts = make(map[kbfsblock.ID]int) 439 for id := range contexts { 440 count := 0 441 entry, ok := b.m[id] 442 if ok { 443 count = entry.refs.getLiveCount() 444 } 445 liveCounts[id] = count 446 } 447 return liveCounts, nil 448 } 449 450 // getAllRefsForTest implements the blockServerLocal interface for 451 // BlockServerMemory. 452 func (b *BlockServerMemory) getAllRefsForTest( 453 ctx context.Context, tlfID tlf.ID) ( 454 map[kbfsblock.ID]blockRefMap, error) { 455 res := make(map[kbfsblock.ID]blockRefMap) 456 b.lock.RLock() 457 defer b.lock.RUnlock() 458 459 if b.m == nil { 460 return nil, errBlockServerMemoryShutdown 461 } 462 463 for id, entry := range b.m { 464 if entry.tlfID != tlfID { 465 continue 466 } 467 res[id] = entry.refs.deepCopy() 468 } 469 return res, nil 470 } 471 472 func (b *BlockServerMemory) numBlocks() int { 473 b.lock.RLock() 474 defer b.lock.RUnlock() 475 return len(b.m) 476 } 477 478 // IsUnflushed implements the BlockServer interface for BlockServerMemory. 479 func (b *BlockServerMemory) IsUnflushed(ctx context.Context, tlfID tlf.ID, 480 _ kbfsblock.ID) (bool, error) { 481 b.lock.RLock() 482 defer b.lock.RUnlock() 483 484 if b.m == nil { 485 return false, errBlockServerMemoryShutdown 486 } 487 488 return false, nil 489 } 490 491 // Shutdown implements the BlockServer interface for BlockServerMemory. 492 func (b *BlockServerMemory) Shutdown(ctx context.Context) { 493 b.lock.Lock() 494 defer b.lock.Unlock() 495 // Make further accesses error out. 496 b.m = nil 497 } 498 499 // RefreshAuthToken implements the BlockServer interface for BlockServerMemory. 500 func (b *BlockServerMemory) RefreshAuthToken(_ context.Context) {} 501 502 // GetUserQuotaInfo implements the BlockServer interface for BlockServerMemory. 503 func (b *BlockServerMemory) GetUserQuotaInfo(ctx context.Context) (info *kbfsblock.QuotaInfo, err error) { 504 if err := checkContext(ctx); err != nil { 505 return nil, err 506 } 507 508 // Return a dummy value here. 509 return &kbfsblock.QuotaInfo{Limit: math.MaxInt64}, nil 510 } 511 512 // GetTeamQuotaInfo implements the BlockServer interface for BlockServerMemory. 513 func (b *BlockServerMemory) GetTeamQuotaInfo( 514 ctx context.Context, _ keybase1.TeamID) ( 515 info *kbfsblock.QuotaInfo, err error) { 516 if err := checkContext(ctx); err != nil { 517 return nil, err 518 } 519 520 // TODO: check team membership and return error if not a reader? 521 522 // Return a dummy value here. 523 return &kbfsblock.QuotaInfo{Limit: math.MaxInt64}, nil 524 }