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