1
1
// Copyright 2019-2022 ChainSafe Systems
2
2
// SPDX-License-Identifier: Apache-2.0, MIT
3
3
4
- use anyhow:: { anyhow, Result } ;
5
4
use byteorder:: { BigEndian , ByteOrder , ReadBytesExt } ;
6
5
use cid:: Cid ;
7
6
use fvm_ipld_blockstore:: { Blockstore , Buffered } ;
@@ -38,33 +37,72 @@ where
38
37
}
39
38
}
40
39
40
+ #[ derive( thiserror:: Error , Debug ) ]
41
+ pub enum Error < E > {
42
+ #[ error( "flush: {0}" ) ]
43
+ Flush ( #[ from] FlushError ) ,
44
+ #[ error( "blockstore: {0}" ) ]
45
+ Blockstore ( E ) ,
46
+ }
47
+
41
48
impl < BS > Buffered for BufferedBlockstore < BS >
42
49
where
43
50
BS : Blockstore ,
44
51
{
45
52
/// Flushes the buffered cache based on the root node.
46
53
/// This will recursively traverse the cache and write all data connected by links to this
47
54
/// root Cid.
48
- fn flush ( & self , root : & Cid ) -> Result < ( ) > {
55
+ fn flush ( & self , root : & Cid ) -> Result < ( ) , Error < BS :: Error > > {
49
56
let mut buffer = Vec :: new ( ) ;
50
57
let mut s = self . write . borrow_mut ( ) ;
51
58
copy_rec ( & s, * root, & mut buffer) ?;
52
59
53
- self . base . put_many_keyed ( buffer) ?;
60
+ self . base
61
+ . put_many_keyed ( buffer)
62
+ . map_err ( Error :: Blockstore ) ?;
54
63
* s = Default :: default ( ) ;
55
64
56
65
Ok ( ( ) )
57
66
}
58
67
}
59
68
69
+ #[ derive( thiserror:: Error , Debug ) ]
70
+ pub enum FlushError {
71
+ #[ error( "io: {0}" ) ]
72
+ Io ( #[ from] std:: io:: Error ) ,
73
+ #[ error( "cid: {0}" ) ]
74
+ Cid ( #[ from] cid:: Error ) ,
75
+ #[ error( "cbor input was not canonical (lval 24 with value < 24)" ) ]
76
+ HeaderLval24 ,
77
+ #[ error( "cbor input was not canonical (lval 25 with value <= MaxUint8)" ) ]
78
+ HeaderLval25 ,
79
+ #[ error( "cbor input was not canonical (lval 26 with value <= MaxUint16)" ) ]
80
+ HeaderLval26 ,
81
+ #[ error( "cbor input was not canonical (lval 27 with value <= MaxUint32)" ) ]
82
+ HeaderLval27 ,
83
+ #[ error( "invalid header cbor_read_header_buf" ) ]
84
+ HeaderInvalid ,
85
+ #[ error( "expected cbor type byte string in input" ) ]
86
+ UnexpectedByteString ,
87
+ #[ error( "string in cbor input too long" ) ]
88
+ StringTooLong ,
89
+ #[ error( "Invalid link ({0}) in flushing buffered store" ) ]
90
+ InvalidLink ( Cid ) ,
91
+ #[ error( "unhandled cbor type: {0}" ) ]
92
+ UnhandledCborType ( u8 ) ,
93
+ }
94
+
60
95
/// Given a CBOR encoded Buffer, returns a tuple of:
61
96
/// the type of the CBOR object along with extra
62
97
/// elements we expect to read. More info on this can be found in
63
98
/// Appendix C. of RFC 7049 which defines the CBOR specification.
64
99
/// This was implemented because the CBOR library we use does not expose low
65
100
/// methods like this, requiring us to deserialize the whole CBOR payload, which
66
101
/// is unnecessary and quite inefficient for our usecase here.
67
- fn cbor_read_header_buf < B : Read > ( br : & mut B , scratch : & mut [ u8 ] ) -> anyhow:: Result < ( u8 , usize ) > {
102
+ fn cbor_read_header_buf < B : Read > (
103
+ br : & mut B ,
104
+ scratch : & mut [ u8 ] ,
105
+ ) -> Result < ( u8 , usize ) , FlushError > {
68
106
let first = br. read_u8 ( ) ?;
69
107
let maj = ( first & 0xe0 ) >> 5 ;
70
108
let low = first & 0x1f ;
@@ -74,49 +112,41 @@ fn cbor_read_header_buf<B: Read>(br: &mut B, scratch: &mut [u8]) -> anyhow::Resu
74
112
} else if low == 24 {
75
113
let val = br. read_u8 ( ) ?;
76
114
if val < 24 {
77
- return Err ( anyhow ! (
78
- "cbor input was not canonical (lval 24 with value < 24)"
79
- ) ) ;
115
+ return Err ( FlushError :: HeaderLval24 ) ;
80
116
}
81
117
Ok ( ( maj, val as usize ) )
82
118
} else if low == 25 {
83
119
br. read_exact ( & mut scratch[ ..2 ] ) ?;
84
120
let val = BigEndian :: read_u16 ( & scratch[ ..2 ] ) ;
85
121
if val <= u8:: MAX as u16 {
86
- return Err ( anyhow ! (
87
- "cbor input was not canonical (lval 25 with value <= MaxUint8)"
88
- ) ) ;
122
+ return Err ( FlushError :: HeaderLval25 ) ;
89
123
}
90
124
Ok ( ( maj, val as usize ) )
91
125
} else if low == 26 {
92
126
br. read_exact ( & mut scratch[ ..4 ] ) ?;
93
127
let val = BigEndian :: read_u32 ( & scratch[ ..4 ] ) ;
94
128
if val <= u16:: MAX as u32 {
95
- return Err ( anyhow ! (
96
- "cbor input was not canonical (lval 26 with value <= MaxUint16)"
97
- ) ) ;
129
+ return Err ( FlushError :: HeaderLval26 ) ;
98
130
}
99
131
Ok ( ( maj, val as usize ) )
100
132
} else if low == 27 {
101
133
br. read_exact ( & mut scratch[ ..8 ] ) ?;
102
134
let val = BigEndian :: read_u64 ( & scratch[ ..8 ] ) ;
103
135
if val <= u32:: MAX as u64 {
104
- return Err ( anyhow ! (
105
- "cbor input was not canonical (lval 27 with value <= MaxUint32)"
106
- ) ) ;
136
+ return Err ( FlushError :: HeaderLval27 ) ;
107
137
}
108
138
Ok ( ( maj, val as usize ) )
109
139
} else {
110
- Err ( anyhow ! ( "invalid header cbor_read_header_buf" ) )
140
+ Err ( FlushError :: HeaderInvalid )
111
141
}
112
142
}
113
143
114
144
/// Given a CBOR serialized IPLD buffer, read through all of it and return all the Links.
115
145
/// This function is useful because it is quite a bit more fast than doing this recursively on a
116
146
/// deserialized IPLD object.
117
- fn scan_for_links < B : Read + Seek , F > ( buf : & mut B , mut callback : F ) -> Result < ( ) >
147
+ fn scan_for_links < B : Read + Seek , F > ( buf : & mut B , mut callback : F ) -> Result < ( ) , FlushError >
118
148
where
119
- F : FnMut ( Cid ) -> anyhow :: Result < ( ) > ,
149
+ F : FnMut ( Cid ) -> Result < ( ) , FlushError > ,
120
150
{
121
151
let mut scratch: [ u8 ; 100 ] = [ 0 ; 100 ] ;
122
152
let mut remaining = 1 ;
@@ -136,10 +166,10 @@ where
136
166
let ( maj, extra) = cbor_read_header_buf ( buf, & mut scratch) ?;
137
167
// The actual CID is expected to be a byte string
138
168
if maj != 2 {
139
- return Err ( anyhow ! ( "expected cbor type byte string in input" ) ) ;
169
+ return Err ( FlushError :: UnexpectedByteString ) ;
140
170
}
141
171
if extra > 100 {
142
- return Err ( anyhow ! ( "string in cbor input too long" ) ) ;
172
+ return Err ( FlushError :: StringTooLong ) ;
143
173
}
144
174
buf. read_exact ( & mut scratch[ ..extra] ) ?;
145
175
let c = Cid :: try_from ( & scratch[ 1 ..extra] ) ?;
@@ -157,7 +187,7 @@ where
157
187
remaining += extra * 2 ;
158
188
}
159
189
_ => {
160
- return Err ( anyhow ! ( "unhandled cbor type: {}" , maj) ) ;
190
+ return Err ( FlushError :: UnhandledCborType ( maj) ) ;
161
191
}
162
192
}
163
193
remaining -= 1 ;
@@ -170,16 +200,14 @@ fn copy_rec<'a>(
170
200
cache : & ' a HashMap < Cid , Vec < u8 > > ,
171
201
root : Cid ,
172
202
buffer : & mut Vec < ( Cid , & ' a [ u8 ] ) > ,
173
- ) -> Result < ( ) > {
203
+ ) -> Result < ( ) , FlushError > {
174
204
// TODO: Make this non-recursive.
175
205
// Skip identity and Filecoin commitment Cids
176
206
if root. codec ( ) != DAG_CBOR {
177
207
return Ok ( ( ) ) ;
178
208
}
179
209
180
- let block = & * cache
181
- . get ( & root)
182
- . ok_or_else ( || anyhow ! ( "Invalid link ({}) in flushing buffered store" , root) ) ?;
210
+ let block = & * cache. get ( & root) . ok_or ( FlushError :: InvalidLink ( root) ) ?;
183
211
184
212
scan_for_links ( & mut Cursor :: new ( block) , |link| {
185
213
if link. codec ( ) != DAG_CBOR {
@@ -205,28 +233,30 @@ impl<BS> Blockstore for BufferedBlockstore<BS>
205
233
where
206
234
BS : Blockstore ,
207
235
{
208
- fn get ( & self , cid : & Cid ) -> Result < Option < Vec < u8 > > > {
236
+ type Error = Error < BS :: Error > ;
237
+
238
+ fn get ( & self , cid : & Cid ) -> Result < Option < Vec < u8 > > , Self :: Error > {
209
239
Ok ( if let Some ( data) = self . write . borrow ( ) . get ( cid) {
210
240
Some ( data. clone ( ) )
211
241
} else {
212
- self . base . get ( cid) ?
242
+ self . base . get ( cid) . map_err ( Error :: Blockstore ) ?
213
243
} )
214
244
}
215
245
216
- fn put_keyed ( & self , cid : & Cid , buf : & [ u8 ] ) -> Result < ( ) > {
246
+ fn put_keyed ( & self , cid : & Cid , buf : & [ u8 ] ) -> Result < ( ) , Self :: Error > {
217
247
self . write . borrow_mut ( ) . insert ( * cid, Vec :: from ( buf) ) ;
218
248
Ok ( ( ) )
219
249
}
220
250
221
- fn has ( & self , k : & Cid ) -> Result < bool > {
251
+ fn has ( & self , k : & Cid ) -> Result < bool , Self :: Error > {
222
252
if self . write . borrow ( ) . contains_key ( k) {
223
253
Ok ( true )
224
254
} else {
225
- Ok ( self . base . has ( k) ?)
255
+ Ok ( self . base . has ( k) . map_err ( Error :: Blockstore ) ?)
226
256
}
227
257
}
228
258
229
- fn put_many_keyed < D , I > ( & self , blocks : I ) -> Result < ( ) >
259
+ fn put_many_keyed < D , I > ( & self , blocks : I ) -> Result < ( ) , Self :: Error >
230
260
where
231
261
Self : Sized ,
232
262
D : AsRef < [ u8 ] > ,
0 commit comments