11// Copyright 2019-2022 ChainSafe Systems
22// SPDX-License-Identifier: Apache-2.0, MIT
33
4- use anyhow:: { anyhow, Result } ;
54use byteorder:: { BigEndian , ByteOrder , ReadBytesExt } ;
65use cid:: Cid ;
76use fvm_ipld_blockstore:: { Blockstore , Buffered } ;
@@ -38,33 +37,72 @@ where
3837 }
3938}
4039
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+
4148impl < BS > Buffered for BufferedBlockstore < BS >
4249where
4350 BS : Blockstore ,
4451{
4552 /// Flushes the buffered cache based on the root node.
4653 /// This will recursively traverse the cache and write all data connected by links to this
4754 /// root Cid.
48- fn flush ( & self , root : & Cid ) -> Result < ( ) > {
55+ fn flush ( & self , root : & Cid ) -> Result < ( ) , Error < BS :: Error > > {
4956 let mut buffer = Vec :: new ( ) ;
5057 let mut s = self . write . borrow_mut ( ) ;
5158 copy_rec ( & s, * root, & mut buffer) ?;
5259
53- self . base . put_many_keyed ( buffer) ?;
60+ self . base
61+ . put_many_keyed ( buffer)
62+ . map_err ( Error :: Blockstore ) ?;
5463 * s = Default :: default ( ) ;
5564
5665 Ok ( ( ) )
5766 }
5867}
5968
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+
6095/// Given a CBOR encoded Buffer, returns a tuple of:
6196/// the type of the CBOR object along with extra
6297/// elements we expect to read. More info on this can be found in
6398/// Appendix C. of RFC 7049 which defines the CBOR specification.
6499/// This was implemented because the CBOR library we use does not expose low
65100/// methods like this, requiring us to deserialize the whole CBOR payload, which
66101/// 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 > {
68106 let first = br. read_u8 ( ) ?;
69107 let maj = ( first & 0xe0 ) >> 5 ;
70108 let low = first & 0x1f ;
@@ -74,49 +112,41 @@ fn cbor_read_header_buf<B: Read>(br: &mut B, scratch: &mut [u8]) -> anyhow::Resu
74112 } else if low == 24 {
75113 let val = br. read_u8 ( ) ?;
76114 if val < 24 {
77- return Err ( anyhow ! (
78- "cbor input was not canonical (lval 24 with value < 24)"
79- ) ) ;
115+ return Err ( FlushError :: HeaderLval24 ) ;
80116 }
81117 Ok ( ( maj, val as usize ) )
82118 } else if low == 25 {
83119 br. read_exact ( & mut scratch[ ..2 ] ) ?;
84120 let val = BigEndian :: read_u16 ( & scratch[ ..2 ] ) ;
85121 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 ) ;
89123 }
90124 Ok ( ( maj, val as usize ) )
91125 } else if low == 26 {
92126 br. read_exact ( & mut scratch[ ..4 ] ) ?;
93127 let val = BigEndian :: read_u32 ( & scratch[ ..4 ] ) ;
94128 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 ) ;
98130 }
99131 Ok ( ( maj, val as usize ) )
100132 } else if low == 27 {
101133 br. read_exact ( & mut scratch[ ..8 ] ) ?;
102134 let val = BigEndian :: read_u64 ( & scratch[ ..8 ] ) ;
103135 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 ) ;
107137 }
108138 Ok ( ( maj, val as usize ) )
109139 } else {
110- Err ( anyhow ! ( "invalid header cbor_read_header_buf" ) )
140+ Err ( FlushError :: HeaderInvalid )
111141 }
112142}
113143
114144/// Given a CBOR serialized IPLD buffer, read through all of it and return all the Links.
115145/// This function is useful because it is quite a bit more fast than doing this recursively on a
116146/// 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 >
118148where
119- F : FnMut ( Cid ) -> anyhow :: Result < ( ) > ,
149+ F : FnMut ( Cid ) -> Result < ( ) , FlushError > ,
120150{
121151 let mut scratch: [ u8 ; 100 ] = [ 0 ; 100 ] ;
122152 let mut remaining = 1 ;
@@ -136,10 +166,10 @@ where
136166 let ( maj, extra) = cbor_read_header_buf ( buf, & mut scratch) ?;
137167 // The actual CID is expected to be a byte string
138168 if maj != 2 {
139- return Err ( anyhow ! ( "expected cbor type byte string in input" ) ) ;
169+ return Err ( FlushError :: UnexpectedByteString ) ;
140170 }
141171 if extra > 100 {
142- return Err ( anyhow ! ( "string in cbor input too long" ) ) ;
172+ return Err ( FlushError :: StringTooLong ) ;
143173 }
144174 buf. read_exact ( & mut scratch[ ..extra] ) ?;
145175 let c = Cid :: try_from ( & scratch[ 1 ..extra] ) ?;
@@ -157,7 +187,7 @@ where
157187 remaining += extra * 2 ;
158188 }
159189 _ => {
160- return Err ( anyhow ! ( "unhandled cbor type: {}" , maj) ) ;
190+ return Err ( FlushError :: UnhandledCborType ( maj) ) ;
161191 }
162192 }
163193 remaining -= 1 ;
@@ -170,16 +200,14 @@ fn copy_rec<'a>(
170200 cache : & ' a HashMap < Cid , Vec < u8 > > ,
171201 root : Cid ,
172202 buffer : & mut Vec < ( Cid , & ' a [ u8 ] ) > ,
173- ) -> Result < ( ) > {
203+ ) -> Result < ( ) , FlushError > {
174204 // TODO: Make this non-recursive.
175205 // Skip identity and Filecoin commitment Cids
176206 if root. codec ( ) != DAG_CBOR {
177207 return Ok ( ( ) ) ;
178208 }
179209
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) ) ?;
183211
184212 scan_for_links ( & mut Cursor :: new ( block) , |link| {
185213 if link. codec ( ) != DAG_CBOR {
@@ -205,28 +233,30 @@ impl<BS> Blockstore for BufferedBlockstore<BS>
205233where
206234 BS : Blockstore ,
207235{
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 > {
209239 Ok ( if let Some ( data) = self . write . borrow ( ) . get ( cid) {
210240 Some ( data. clone ( ) )
211241 } else {
212- self . base . get ( cid) ?
242+ self . base . get ( cid) . map_err ( Error :: Blockstore ) ?
213243 } )
214244 }
215245
216- fn put_keyed ( & self , cid : & Cid , buf : & [ u8 ] ) -> Result < ( ) > {
246+ fn put_keyed ( & self , cid : & Cid , buf : & [ u8 ] ) -> Result < ( ) , Self :: Error > {
217247 self . write . borrow_mut ( ) . insert ( * cid, Vec :: from ( buf) ) ;
218248 Ok ( ( ) )
219249 }
220250
221- fn has ( & self , k : & Cid ) -> Result < bool > {
251+ fn has ( & self , k : & Cid ) -> Result < bool , Self :: Error > {
222252 if self . write . borrow ( ) . contains_key ( k) {
223253 Ok ( true )
224254 } else {
225- Ok ( self . base . has ( k) ?)
255+ Ok ( self . base . has ( k) . map_err ( Error :: Blockstore ) ?)
226256 }
227257 }
228258
229- fn put_many_keyed < D , I > ( & self , blocks : I ) -> Result < ( ) >
259+ fn put_many_keyed < D , I > ( & self , blocks : I ) -> Result < ( ) , Self :: Error >
230260 where
231261 Self : Sized ,
232262 D : AsRef < [ u8 ] > ,
0 commit comments