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