1pub mod crc;
27mod persist;
28use std::fmt::Debug;
29use std::io::{Read, Write};
30
31use bincode::config;
32use bincode::config::{Configuration, Fixint, Limit, LittleEndian};
33use bincode::error::{DecodeError, EncodeError};
34use crc64::crc64;
35use semver::Version;
36use serde::de::DeserializeOwned;
37use serde::{Deserialize, Serialize};
38
39use crate::persist::SNAPSHOT_VERSION;
40use crate::snapshot::crc::CRC64Writer;
41pub use crate::snapshot::persist::Persist;
42use crate::utils::mib_to_bytes;
43
44#[cfg(target_arch = "x86_64")]
45const SNAPSHOT_MAGIC_ID: u64 = 0x0710_1984_8664_0000u64;
46
47const DESERIALIZATION_BYTES_LIMIT: usize = mib_to_bytes(10);
49
50const BINCODE_CONFIG: Configuration<LittleEndian, Fixint, Limit<DESERIALIZATION_BYTES_LIMIT>> =
51 config::standard()
52 .with_fixed_int_encoding()
53 .with_limit::<DESERIALIZATION_BYTES_LIMIT>()
54 .with_little_endian();
55
56#[cfg(target_arch = "aarch64")]
57const SNAPSHOT_MAGIC_ID: u64 = 0x0710_1984_AAAA_0000u64;
58
59#[derive(Debug, thiserror::Error, displaydoc::Display)]
61pub enum SnapshotError {
62 Crc64,
64 InvalidFormatVersion(Version),
66 InvalidMagic(u64),
68 Encode(#[from] EncodeError),
70 Decode(#[from] DecodeError),
72 Io(#[from] std::io::Error),
74}
75
76fn serialize<S: Serialize, W: Write>(data: &S, write: &mut W) -> Result<(), SnapshotError> {
77 bincode::serde::encode_into_std_write(data, write, BINCODE_CONFIG)
78 .map_err(SnapshotError::Encode)
79 .map(|_| ())
80}
81
82#[derive(Debug, Serialize, Deserialize)]
84struct SnapshotHdr {
85 magic: u64,
87 version: Version,
89}
90
91impl SnapshotHdr {
92 fn load(buf: &mut &[u8]) -> Result<Self, SnapshotError> {
93 let (hdr, bytes_read) = bincode::serde::decode_from_slice::<Self, _>(buf, BINCODE_CONFIG)?;
94
95 if hdr.magic != SNAPSHOT_MAGIC_ID {
96 return Err(SnapshotError::InvalidMagic(hdr.magic));
97 }
98
99 if hdr.version.major != SNAPSHOT_VERSION.major || hdr.version.minor > SNAPSHOT_VERSION.minor
100 {
101 return Err(SnapshotError::InvalidFormatVersion(hdr.version));
102 }
103
104 *buf = &buf[bytes_read..];
105
106 Ok(hdr)
107 }
108}
109
110pub fn get_format_version<R: Read>(reader: &mut R) -> Result<Version, SnapshotError> {
113 let hdr: SnapshotHdr = bincode::serde::decode_from_std_read(reader, BINCODE_CONFIG)?;
114 Ok(hdr.version)
115}
116
117#[derive(Debug, Serialize)]
121pub struct Snapshot<Data> {
122 header: SnapshotHdr,
123 pub data: Data,
125}
126
127impl<Data> Snapshot<Data> {
128 pub fn new(data: Data) -> Self {
130 Self {
131 header: SnapshotHdr {
132 magic: SNAPSHOT_MAGIC_ID,
133 version: SNAPSHOT_VERSION.clone(),
134 },
135 data,
136 }
137 }
138
139 pub fn version(&self) -> &Version {
141 &self.header.version
142 }
143}
144
145impl<Data: DeserializeOwned> Snapshot<Data> {
146 pub(crate) fn load_without_crc_check(mut buf: &[u8]) -> Result<Self, SnapshotError> {
147 let header = SnapshotHdr::load(&mut buf)?;
148 let data = bincode::serde::decode_from_slice(buf, BINCODE_CONFIG)?.0;
149 Ok(Self { header, data })
150 }
151
152 pub fn load<R: Read>(reader: &mut R) -> Result<Self, SnapshotError> {
155 let mut buf = Vec::new();
158 reader.read_to_end(&mut buf)?;
159 let snapshot = Self::load_without_crc_check(buf.as_slice())?;
160 let computed_checksum = crc64(0, buf.as_slice());
161 if computed_checksum != 0 {
165 return Err(SnapshotError::Crc64);
166 }
167 Ok(snapshot)
168 }
169}
170
171impl<Data: Serialize> Snapshot<Data> {
172 pub fn save<W: Write>(&self, writer: &mut W) -> Result<(), SnapshotError> {
175 let mut crc_writer = CRC64Writer::new(writer);
176 serialize(self, &mut crc_writer)?;
177 serialize(&crc_writer.checksum(), crc_writer.writer)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::persist::MicrovmState;
185
186 #[test]
187 fn test_snapshot_restore() {
188 let state = MicrovmState::default();
189 let mut buf = Vec::new();
190
191 Snapshot::new(state).save(&mut buf).unwrap();
192 Snapshot::<MicrovmState>::load(&mut buf.as_slice()).unwrap();
193 }
194
195 #[test]
196 fn test_parse_version_from_file() {
197 let snapshot = Snapshot::new(42);
198
199 let mut snapshot_data = vec![0u8; 100];
201
202 snapshot.save(&mut snapshot_data.as_mut_slice()).unwrap();
203
204 assert_eq!(
205 get_format_version(&mut snapshot_data.as_slice()).unwrap(),
206 SNAPSHOT_VERSION
207 );
208 }
209
210 #[test]
211 fn test_bad_reader() {
212 #[derive(Debug)]
213 struct BadReader;
214
215 impl Read for BadReader {
216 fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
217 Err(std::io::ErrorKind::InvalidInput.into())
218 }
219 }
220
221 let mut reader = BadReader {};
222
223 assert!(
224 matches!(Snapshot::<()>::load(&mut reader), Err(SnapshotError::Io(inner)) if inner.kind() == std::io::ErrorKind::InvalidInput)
225 );
226 }
227
228 #[test]
229 fn test_bad_magic() {
230 let mut data = vec![0u8; 100];
231
232 let snapshot = Snapshot::new(());
233 snapshot.save(&mut data.as_mut_slice()).unwrap();
234
235 data[0] = 0x01;
238 data[1] = 0x02;
239 data[2] = 0x03;
240 data[3] = 0x04;
241 data[4] = 0x42;
242 data[5] = 0x43;
243 data[6] = 0x44;
244 data[7] = 0x45;
245 assert!(matches!(
246 SnapshotHdr::load(&mut data.as_slice()),
247 Err(SnapshotError::InvalidMagic(0x4544_4342_0403_0201u64))
248 ));
249 }
250
251 #[test]
252 fn test_bad_crc() {
253 let mut data = vec![0u8; 100];
254
255 let snapshot = Snapshot::new(());
256 serialize(&snapshot, &mut data.as_mut_slice()).unwrap();
259
260 assert!(matches!(
261 Snapshot::<()>::load(&mut std::io::Cursor::new(data.as_slice())),
262 Err(SnapshotError::Crc64)
263 ));
264 }
265
266 #[test]
267 fn test_bad_version() {
268 let mut data = vec![0u8; 100];
269
270 let mut snapshot = Snapshot::new(());
272 snapshot.header.version.major = SNAPSHOT_VERSION.major + 1;
273 snapshot.save(&mut data.as_mut_slice()).unwrap();
274
275 assert!(matches!(
276 Snapshot::<()>::load_without_crc_check(data.as_slice()),
277 Err(SnapshotError::InvalidFormatVersion(v)) if v.major == SNAPSHOT_VERSION.major + 1
278 ));
279
280 let mut snapshot = Snapshot::new(());
282 snapshot.header.version.minor = SNAPSHOT_VERSION.minor + 1;
283 snapshot.save(&mut data.as_mut_slice()).unwrap();
284 assert!(matches!(
285 Snapshot::<()>::load_without_crc_check(data.as_slice()),
286 Err(SnapshotError::InvalidFormatVersion(v)) if v.minor == SNAPSHOT_VERSION.minor + 1
287 ));
288
289 let snapshot = Snapshot::new(());
292 snapshot.save(&mut data.as_mut_slice()).unwrap();
293 Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();
294
295 if SNAPSHOT_VERSION.minor != 0 {
296 let mut snapshot = Snapshot::new(());
297 snapshot.header.version.minor = SNAPSHOT_VERSION.minor - 1;
298 snapshot.save(&mut data.as_mut_slice()).unwrap();
299 Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();
300 }
301
302 let mut snapshot = Snapshot::new(());
303 snapshot.header.version.patch = 0;
304 snapshot.save(&mut data.as_mut_slice()).unwrap();
305 Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();
306
307 let mut snapshot = Snapshot::new(());
308 snapshot.header.version.patch = SNAPSHOT_VERSION.patch + 1;
309 snapshot.save(&mut data.as_mut_slice()).unwrap();
310 Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();
311
312 let mut snapshot = Snapshot::new(());
313 snapshot.header.version.patch = 1024;
314 snapshot.save(&mut data.as_mut_slice()).unwrap();
315 Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();
316 }
317}