@@ -30,6 +30,7 @@ use std::io::{Read, Write};
30
30
31
31
use bincode:: config;
32
32
use bincode:: config:: { Configuration , Fixint , Limit , LittleEndian } ;
33
+ use bincode:: error:: { DecodeError , EncodeError } ;
33
34
use semver:: Version ;
34
35
use serde:: de:: DeserializeOwned ;
35
36
use serde:: { Deserialize , Serialize } ;
@@ -72,35 +73,37 @@ pub enum SnapshotError {
72
73
73
74
/// Firecracker snapshot header
74
75
#[ derive( Debug , Serialize , Deserialize ) ]
75
- pub struct SnapshotHdr {
76
- /// magic value
76
+ struct SnapshotHdr {
77
+ /// Magic value
77
78
magic : u64 ,
78
79
/// Snapshot data version
79
80
version : Version ,
80
81
}
81
82
82
83
impl SnapshotHdr {
83
- pub fn new ( version : Version ) -> Self {
84
+ /// Create a new header for writing snapshots
85
+ fn new ( version : Version ) -> Self {
84
86
Self {
85
87
magic : SNAPSHOT_MAGIC_ID ,
86
88
version,
87
89
}
88
90
}
89
91
90
- pub fn load < R : Read > ( reader : & mut R ) -> Result < Self , SnapshotError > {
92
+ /// Load and deserialize just the header (magic + version)
93
+ fn load < R : Read > ( reader : & mut R ) -> Result < Self , SnapshotError > {
91
94
let hdr: SnapshotHdr = deserialize ( reader) ?;
92
-
93
- Ok ( hdr)
95
+ if hdr. magic != SNAPSHOT_MAGIC_ID {
96
+ Err ( SnapshotError :: InvalidMagic ( hdr. magic ) )
97
+ }
98
+ else {
99
+ Ok ( hdr)
100
+ }
94
101
}
95
102
96
- /// Helper function to deserialize an object from a reader
97
- pub fn deserialize < T , O > ( reader : & mut T ) -> Result < O , SnapshotError >
98
- where
99
- T : Read ,
100
- O : DeserializeOwned + Debug ,
101
- {
102
- bincode:: serde:: decode_from_std_read ( reader, BINCODE_CONFIG )
103
- . map_err ( |err| SnapshotError :: Serde ( err. to_string ( ) ) )
103
+ /// Serialize and write just the header
104
+ fn store < W : Write > ( & self , writer : & mut W ) -> Result < ( ) , SnapshotError > {
105
+ serialize ( writer, self ) ?;
106
+ Ok ( ( ) )
104
107
}
105
108
}
106
109
@@ -116,51 +119,44 @@ impl SnapshotHdr {
116
119
Ok ( ( ) )
117
120
}
118
121
119
- impl < Data : DeserializeOwned > Snapshot < Data > {
120
- pub fn load_unchecked < R : Read > ( reader : & mut R ) -> Result < Self , SnapshotError >
121
- where
122
- Data : DeserializeOwned + Debug ,
123
- {
124
- let hdr: SnapshotHdr = deserialize ( reader) ?;
125
- if hdr. magic != SNAPSHOT_MAGIC_ID {
126
- return Err ( SnapshotError :: InvalidMagic ( hdr. magic ) ) ;
127
- }
128
-
129
- let data: Data = deserialize ( reader) ?;
122
+ // Implementations for deserializing snapshots
123
+ // Publicly exposed functions:
124
+ // - load_unchecked()
125
+ //- load()
126
+ impl < Data : DeserializeOwned + Debug > Snapshot < Data > {
127
+ /// Load without CRC or version‐check, but verify magic via `SnapshotHdr::load`.
128
+ pub fn load_unchecked < R : Read + Debug > ( reader : & mut R ) -> Result < Self , SnapshotError > {
129
+ // this calls `deserialize` + checks magic internally
130
+ let hdr: SnapshotHdr = SnapshotHdr :: load ( reader) ?;
131
+ let data: Data = deserialize ( reader) ?;
130
132
Ok ( Self { header : hdr, data } )
131
133
}
132
134
133
- pub fn load < R : Read > ( reader : & mut R , snapshot_len : usize ) -> Result < Self , SnapshotError >
134
- where
135
- Data : DeserializeOwned + Debug ,
136
- {
135
+ /// Load with CRC64 validation in one pass, using `load_unchecked` for header+data.
136
+ pub fn load < R : Read + Debug > ( reader : & mut R ) -> Result < Self , SnapshotError > {
137
+ // 1) Wrap in CRC reader
137
138
let mut crc_reader = CRC64Reader :: new ( reader) ;
138
139
139
- // Fail-fast if the snapshot length is too small
140
- let raw_snapshot_len = snapshot_len
141
- . checked_sub ( std:: mem:: size_of :: < u64 > ( ) )
142
- . ok_or ( SnapshotError :: InvalidSnapshotSize ) ?;
143
-
144
- // Read everything apart from the CRC.
145
- let mut snapshot = vec ! [ 0u8 ; raw_snapshot_len] ;
146
- crc_reader
147
- . read_exact ( & mut snapshot)
148
- . map_err ( |ref err| SnapshotError :: Io ( err. raw_os_error ( ) . unwrap_or ( libc:: EINVAL ) ) ) ?;
149
-
150
- // Since the reader updates the checksum as bytes ar being read from it, the order of these
151
- // 2 statements is important, we first get the checksum computed on the read bytes
152
- // then read the stored checksum.
153
- let computed_checksum = crc_reader. checksum ( ) ;
154
- let stored_checksum: u64 = deserialize ( & mut crc_reader) ?;
155
- if computed_checksum != stored_checksum {
156
- return Err ( SnapshotError :: Crc64 ( computed_checksum) ) ;
140
+ // 2) Parse header + payload & magic‐check
141
+ let snapshot = Snapshot :: load_unchecked ( & mut crc_reader) ?;
142
+
143
+ // 3) Grab the computed CRC over everything read so far
144
+ let computed = crc_reader. checksum ( ) ;
145
+
146
+ // 4) Deserialize the trailing u64 and compare
147
+ let stored: u64 = deserialize ( & mut crc_reader) ?;
148
+ if stored != computed {
149
+ return Err ( SnapshotError :: Crc64 ( computed) ) ;
157
150
}
158
151
159
- let mut snapshot_slice: & [ u8 ] = snapshot. as_mut_slice ( ) ;
160
- Snapshot :: load_unchecked :: < _ > ( & mut snapshot_slice)
152
+ Ok ( snapshot)
161
153
}
162
154
}
163
155
156
+ // Implementations for serializing snapshots
157
+ // Publicly-exposed *methods*:
158
+ // - save(self,...)
159
+ // - save_with_crc(self,...)
164
160
impl < Data : Serialize + Debug > Snapshot < Data > {
165
161
pub fn save < W : Write > ( & self , mut writer : & mut W ) -> Result < usize , SnapshotError > {
166
162
// Write magic value and snapshot version
@@ -179,8 +175,12 @@ impl<Data: Serialize + Debug> Snapshot<Data> {
179
175
}
180
176
}
181
177
178
+ // General methods for snapshots (related to serialization, see above, since an
179
+ // instance is needed to serialize)
182
180
impl < Data > Snapshot < Data > {
183
- pub fn new ( header : SnapshotHdr , data : Data ) -> Self {
181
+ /// Construct from a pre‐built header + payload
182
+ pub fn new ( version : Version , data : Data ) -> Self {
183
+ header = SnapshotHdr :: new ( version) ;
184
184
Snapshot { header, data }
185
185
}
186
186
@@ -189,35 +189,56 @@ impl<Data> Snapshot<Data> {
189
189
}
190
190
}
191
191
192
- /// Helper function to deserialize an object from a reader
192
+ /// Deserialize any `O: DeserializeOwned + Debug` via bincode + our config,
193
193
fn deserialize < T , O > ( reader : & mut T ) -> Result < O , SnapshotError >
194
194
where
195
195
T : Read ,
196
196
O : DeserializeOwned + Debug ,
197
197
{
198
- // flags below are those used by default by bincode::deserialize_from, plus `with_limit`.
199
- bincode:: DefaultOptions :: new ( )
200
- . with_limit ( VM_STATE_DESERIALIZE_LIMIT )
201
- . with_fixint_encoding ( )
202
- . allow_trailing_bytes ( ) // need this because we deserialize header and snapshot from the same file, so after
203
- // reading the header, there will be trailing bytes.
204
- . deserialize_from ( reader)
205
- . map_err ( |err| SnapshotError :: Serde ( err. to_string ( ) ) )
198
+ bincode:: serde:: decode_from_std_read ( reader, BINCODE_CONFIG )
199
+ . map_err ( |err| match err {
200
+ // The reader hit an actual IO error.
201
+ DecodeError :: Io { inner, .. } =>
202
+ SnapshotError :: Io ( inner. raw_os_error ( ) . unwrap_or ( EIO ) ) ,
203
+
204
+ // Not enough bytes in the input for what we expected.
205
+ DecodeError :: UnexpectedEnd { .. } |
206
+ DecodeError :: LimitExceeded =>
207
+ SnapshotError :: InvalidSnapshotSize ,
208
+
209
+ // Anything else is a ser/de format issue.
210
+ other =>
211
+ SnapshotError :: Serde ( other. to_string ( ) ) ,
212
+ } )
206
213
}
207
214
208
- /// Helper function to serialize an object to a writer
215
+ /// Serialize any `O: Serialize + Debug` into a Vec, write it, and return the byte‐count,
209
216
fn serialize < T , O > ( writer : & mut T , data : & O ) -> Result < usize , SnapshotError >
210
217
where
211
218
T : Write ,
212
219
O : Serialize + Debug ,
213
220
{
214
- let mut buffer = Vec :: new ( ) ;
215
- bincode:: serialize_into ( & mut buffer, data)
216
- . map_err ( |err| SnapshotError :: Serde ( err. to_string ( ) ) ) ?;
217
-
221
+ // 1) Encode into an in-memory buffer
222
+ let mut buf = Vec :: new ( ) ;
223
+ bincode:: serde:: encode_into_std_write ( data, & mut buf, BINCODE_CONFIG )
224
+ . map_err ( |err| match err {
225
+ // Ran out of room while encoding
226
+ EncodeError :: UnexpectedEnd =>
227
+ SnapshotError :: Io ( libc:: EIO ) ,
228
+
229
+ // Underlying IO failure during encode (index tells how many bytes got written)
230
+ EncodeError :: Io { inner, .. } =>
231
+ SnapshotError :: Io ( inner. raw_os_error ( ) . unwrap_or ( libc:: EIO ) ) ,
232
+
233
+ // Any other encode error we surface as Serde
234
+ other =>
235
+ SnapshotError :: Serde ( other. to_string ( ) ) ,
236
+ } ) ?;
237
+
238
+ // 2) Flush that buffer to the target writer
218
239
writer
219
- . write_all ( & buffer )
220
- . map_err ( |err | SnapshotError :: Serde ( err . to_string ( ) ) ) ?;
240
+ . write_all ( & buf )
241
+ . map_err ( |io_err | SnapshotError :: Io ( io_err . raw_os_error ( ) . unwrap_or ( libc :: EIO ) ) ) ?;
221
242
222
243
Ok ( buffer. len ( ) )
223
244
// bincode::serialize_into(writer, data).map_err(|err| SnapshotError::Serde(err.to_string()))
0 commit comments