github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/dirty_bcache_disk.go (about) 1 // Copyright 2018 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 "context" 9 "sync" 10 11 "github.com/keybase/client/go/kbfs/data" 12 "github.com/keybase/client/go/kbfs/libkey" 13 "github.com/keybase/client/go/kbfs/tlf" 14 "github.com/pkg/errors" 15 ) 16 17 type dirtyBlockCacheDiskConfig interface { 18 codecGetter 19 cryptoPureGetter 20 keyGetterGetter 21 blockOpsGetter 22 } 23 24 type dirtyBlockCacheDiskInfo struct { 25 tmpPtr data.BlockPointer 26 isDir bool 27 } 28 29 func (dbcdi dirtyBlockCacheDiskInfo) newBlock() data.Block { 30 if dbcdi.isDir { 31 return data.NewDirBlock() 32 } 33 return data.NewFileBlock() 34 } 35 36 // DirtyBlockCacheDisk stores dirty blocks in a local disk block 37 // cache, rather than keeping them in memory. 38 type DirtyBlockCacheDisk struct { 39 config dirtyBlockCacheDiskConfig 40 diskCache *DiskBlockCacheLocal 41 kmd libkey.KeyMetadata 42 branch data.BranchName 43 44 lock sync.RWMutex 45 blocks map[data.BlockPointer]dirtyBlockCacheDiskInfo 46 } 47 48 var _ data.DirtyBlockCacheSimple = (*DirtyBlockCacheDisk)(nil) 49 50 func newDirtyBlockCacheDisk( 51 config dirtyBlockCacheDiskConfig, 52 diskCache *DiskBlockCacheLocal, kmd libkey.KeyMetadata, 53 branch data.BranchName) *DirtyBlockCacheDisk { 54 return &DirtyBlockCacheDisk{ 55 config: config, 56 diskCache: diskCache, 57 kmd: kmd, 58 branch: branch, 59 blocks: make(map[data.BlockPointer]dirtyBlockCacheDiskInfo), 60 } 61 } 62 63 func (d *DirtyBlockCacheDisk) getInfo(ptr data.BlockPointer) ( 64 dirtyBlockCacheDiskInfo, bool) { 65 d.lock.RLock() 66 defer d.lock.RUnlock() 67 info, ok := d.blocks[ptr] 68 return info, ok 69 } 70 71 func (d *DirtyBlockCacheDisk) saveInfo( 72 ptr data.BlockPointer, info dirtyBlockCacheDiskInfo) { 73 d.lock.Lock() 74 defer d.lock.Unlock() 75 d.blocks[ptr] = info 76 } 77 78 // Get implements the DirtyBlockCache interface for 79 // DirtyBlockCacheDisk. 80 func (d *DirtyBlockCacheDisk) Get( 81 ctx context.Context, tlfID tlf.ID, ptr data.BlockPointer, branch data.BranchName) ( 82 data.Block, error) { 83 if branch != d.branch { 84 return nil, errors.Errorf( 85 "Branch %s doesn't match branch %s", branch, d.branch) 86 } 87 88 info, ok := d.getInfo(ptr) 89 if !ok { 90 return nil, data.NoSuchBlockError{ID: ptr.ID} 91 } 92 93 // Look it up under the temp ID, which is an actual hash that can 94 // be verified. 95 data, serverHalf, _, err := d.diskCache.Get(ctx, tlfID, info.tmpPtr.ID) 96 if err != nil { 97 return nil, err 98 } 99 100 block := info.newBlock() 101 err = assembleBlockLocal( 102 ctx, d.config.keyGetter(), d.config.Codec(), 103 d.config.cryptoPure(), d.kmd, info.tmpPtr, block, data, serverHalf) 104 if err != nil { 105 return nil, err 106 } 107 return block, nil 108 } 109 110 // Put implements the DirtyBlockCache interface for 111 // DirtyBlockCacheDisk. Note than any modifications made to `block` 112 // after the `Put` will require another `Put` call, in order for them 113 // to be reflected in the next `Get` call for that block pointer. 114 func (d *DirtyBlockCacheDisk) Put( 115 ctx context.Context, tlfID tlf.ID, ptr data.BlockPointer, 116 branch data.BranchName, block data.Block) error { 117 if branch != d.branch { 118 return errors.Errorf( 119 "Branch %s doesn't match branch %s", branch, d.branch) 120 } 121 122 // Need to ready the block, since the disk cache expects encrypted 123 // data and a block ID that can be verified against that data. 124 id, _, readyBlockData, err := d.config.BlockOps().Ready(ctx, d.kmd, block) 125 if err != nil { 126 return err 127 } 128 129 err = d.diskCache.Put( 130 ctx, tlfID, id, readyBlockData.Buf, readyBlockData.ServerHalf) 131 if err != nil { 132 return err 133 } 134 135 directType := data.DirectBlock 136 if block.IsIndirect() { 137 directType = data.IndirectBlock 138 } 139 _, isDir := block.(*data.DirBlock) 140 141 info := dirtyBlockCacheDiskInfo{ 142 tmpPtr: data.BlockPointer{ 143 ID: id, 144 KeyGen: d.kmd.LatestKeyGeneration(), 145 DataVer: block.DataVersion(), 146 DirectType: directType, 147 }, 148 isDir: isDir, 149 } 150 d.saveInfo(ptr, info) 151 152 // TODO: have an in-memory LRU cache of limited size to optimize 153 // frequent block access? 154 return nil 155 }