dbs_snapshot/
lib.rs

1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4#![deny(missing_docs)]
5
6//! Provides version tolerant serialization and deserialization facilities and
7//! implements a persistent storage format for Firecracker state snapshots.
8//!
9//! The `Snapshot` API manages serialization and deserialization of collections of objects
10//! that implement the `Versionize` trait.
11//!
12//!  |----------------------------|
13//!  |       64 bit magic_id      |
14//!  |----------------------------|
15//!  |         SnapshotHdr        |
16//!  |----------------------------|
17//!  |          State             |
18//!  |----------------------------|
19//!  |        optional CRC64      |
20//!  |----------------------------|
21//!
22//! Each structure, union or enum is versioned separately and only needs to increment their version
23//! if a field is added or removed. For each state snapshot we define 2 versions:
24//!  - **the format version** which refers to the SnapshotHdr, CRC, or the representation of
25//!    primitives types (currently we use versionize that uses serde bincode as a backend). The current
26//!    implementation does not have any logic dependent on it.
27//!  - **the data version** which refers to the state.
28mod persist;
29use std::fmt::Debug;
30use std::io::{Read, Write};
31
32use versionize::crc::{CRC64Reader, CRC64Writer};
33use versionize::{VersionMap, Versionize, VersionizeResult};
34use versionize_derive::Versionize;
35
36pub use crate::persist::Persist;
37
38const BASE_MAGIC_ID_MASK: u64 = !0xFFFFu64;
39
40#[cfg(target_arch = "x86_64")]
41const BASE_MAGIC_ID: u64 = 0x0710_1984_8664_0000u64;
42
43#[cfg(target_arch = "aarch64")]
44const BASE_MAGIC_ID: u64 = 0x0710_1984_AAAA_0000u64;
45
46#[cfg(target_arch = "powerpc64")]
47const BASE_MAGIC_ID: u64 = 0x0710_1984_CC64_0000u64;
48
49#[cfg(target_arch = "riscv64")]
50const BASE_MAGIC_ID: u64 = 0x0710_1984_C564_0000u64;
51
52/// Error definitions for the Snapshot API.
53#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq)]
54pub enum Error {
55    /// CRC64 validation failed: {0}
56    Crc64(u64),
57    /// Invalid data version: {0}
58    InvalidDataVersion(u16),
59    /// Invalid format version: {0}
60    InvalidFormatVersion(u16),
61    /// Magic value does not match arch: {0}
62    InvalidMagic(u64),
63    /// Snapshot file is smaller than CRC length.
64    InvalidSnapshotSize,
65    /// An IO error occurred: {0}
66    Io(i32),
67    /// A versioned serialization/deserialization error occurred: {0}
68    Versionize(versionize::VersionizeError),
69}
70
71#[derive(Default, Debug, Versionize)]
72struct SnapshotHdr {
73    /// Snapshot data version (firecracker version).
74    data_version: u16,
75}
76
77/// The `Snapshot` API manages serialization and deserialization of collections of objects
78/// that implement the `Versionize` trait.
79#[derive(Debug)]
80pub struct Snapshot {
81    hdr: SnapshotHdr,
82    version_map: VersionMap,
83    // Required for serialization.
84    target_version: u16,
85}
86
87// Parse a magic_id and return the format version.
88fn get_format_version(magic_id: u64) -> Result<u16, Error> {
89    let magic_arch = magic_id & BASE_MAGIC_ID_MASK;
90    if magic_arch == BASE_MAGIC_ID {
91        return Ok((magic_id & !BASE_MAGIC_ID_MASK) as u16);
92    }
93    Err(Error::InvalidMagic(magic_id))
94}
95
96fn build_magic_id(format_version: u16) -> u64 {
97    BASE_MAGIC_ID | u64::from(format_version)
98}
99
100impl Snapshot {
101    /// Creates a new instance which can only be used to save a new snapshot.
102    pub fn new(version_map: VersionMap, target_version: u16) -> Snapshot {
103        Snapshot {
104            version_map,
105            hdr: SnapshotHdr::default(),
106            target_version,
107        }
108    }
109
110    /// Fetches snapshot data version.
111    pub fn get_data_version<T>(mut reader: &mut T, version_map: &VersionMap) -> Result<u16, Error>
112    where
113        T: Read + Debug,
114    {
115        let format_version_map = Self::format_version_map();
116        let magic_id =
117            <u64 as Versionize>::deserialize(&mut reader, &format_version_map, 0 /* unused */)
118                .map_err(Error::Versionize)?;
119
120        let format_version = get_format_version(magic_id)?;
121        if format_version > format_version_map.latest_version() || format_version == 0 {
122            return Err(Error::InvalidFormatVersion(format_version));
123        }
124
125        let hdr: SnapshotHdr =
126            SnapshotHdr::deserialize(&mut reader, &format_version_map, format_version)
127                .map_err(Error::Versionize)?;
128        if hdr.data_version > version_map.latest_version() || hdr.data_version == 0 {
129            return Err(Error::InvalidDataVersion(hdr.data_version));
130        }
131
132        Ok(hdr.data_version)
133    }
134
135    /// Attempts to load an existing snapshot without CRC validation.
136    pub fn unchecked_load<T: Read + Debug, O: Versionize + Debug>(
137        mut reader: &mut T,
138        version_map: VersionMap,
139    ) -> Result<(O, u16), Error> {
140        let data_version = Self::get_data_version(&mut reader, &version_map)?;
141        let res =
142            O::deserialize(&mut reader, &version_map, data_version).map_err(Error::Versionize)?;
143        Ok((res, data_version))
144    }
145
146    /// Attempts to load an existing snapshot and validate CRC.
147    pub fn load<T: Read + Debug, O: Versionize + Debug>(
148        reader: &mut T,
149        snapshot_len: usize,
150        version_map: VersionMap,
151    ) -> Result<(O, u16), Error> {
152        let mut crc_reader = CRC64Reader::new(reader);
153
154        // Extract snapshot data without stored checksum, which is 8 bytes in size
155        let raw_snapshot_len = snapshot_len
156            .checked_sub(std::mem::size_of::<u64>())
157            .ok_or(Error::InvalidSnapshotSize)?;
158        let mut snapshot = vec![0u8; raw_snapshot_len];
159        crc_reader
160            .read_exact(&mut snapshot)
161            .map_err(|ref err| Error::Io(err.raw_os_error().unwrap_or(libc::EINVAL)))?;
162
163        // Since the reader updates the checksum as bytes ar being read from it, the order of these
164        // 2 statements is important, we first get the checksum computed on the read bytes
165        // then read the stored checksum.
166        let computed_checksum = crc_reader.checksum();
167        let format_vm = Self::format_version_map();
168        let stored_checksum: u64 =
169            Versionize::deserialize(&mut crc_reader, &format_vm, 0).map_err(Error::Versionize)?;
170        if computed_checksum != stored_checksum {
171            return Err(Error::Crc64(computed_checksum));
172        }
173
174        let mut snapshot_slice: &[u8] = snapshot.as_mut_slice();
175        Snapshot::unchecked_load::<_, O>(&mut snapshot_slice, version_map)
176    }
177
178    /// Saves a snapshot and include a CRC64 checksum.
179    pub fn save<T, O>(&mut self, writer: &mut T, object: &O) -> Result<(), Error>
180    where
181        T: Write + Debug,
182        O: Versionize + Debug,
183    {
184        let mut crc_writer = CRC64Writer::new(writer);
185        self.save_without_crc(&mut crc_writer, object)?;
186
187        let checksum = crc_writer.checksum();
188        checksum
189            .serialize(&mut crc_writer, &Self::format_version_map(), 0)
190            .map_err(Error::Versionize)?;
191        Ok(())
192    }
193
194    // TODO Remove `skip(crc_writer)` when https://github.com/firecracker-microvm/versionize/pull/59
195    // is merged and included.
196    /// Save a snapshot with no CRC64 checksum included.
197    pub fn save_without_crc<T, O>(&mut self, mut writer: &mut T, object: &O) -> Result<(), Error>
198    where
199        T: Write,
200        O: Versionize + Debug,
201    {
202        self.hdr = SnapshotHdr {
203            data_version: self.target_version,
204        };
205
206        let format_version_map = Self::format_version_map();
207        let magic_id = build_magic_id(format_version_map.latest_version());
208
209        // Serialize magic id using the format version map.
210        magic_id
211            .serialize(&mut writer, &format_version_map, 0 /* unused */)
212            .map_err(Error::Versionize)?;
213
214        // Serialize header using the format version map.
215        self.hdr
216            .serialize(
217                &mut writer,
218                &format_version_map,
219                format_version_map.latest_version(),
220            )
221            .map_err(Error::Versionize)?;
222
223        // Serialize the object using the state version map.
224        object
225            .serialize(&mut writer, &self.version_map, self.target_version)
226            .map_err(Error::Versionize)?;
227        writer
228            .flush()
229            .map_err(|ref err| Error::Io(err.raw_os_error().unwrap_or(libc::EINVAL)))
230    }
231
232    // Returns the current snapshot format version.
233    // Not to be confused with data version which refers to the aplication
234    // defined structures.
235    // This version map allows us to change the underlying storage format -
236    // for example the way we encode vectors or moving to something else than bincode.
237    fn format_version_map() -> VersionMap {
238        // Firecracker snapshot format version 1.
239        VersionMap::new()
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[derive(Clone, Debug, Versionize)]
248    pub struct Test1 {
249        field_x: u64,
250        field0: u64,
251        field1: u32,
252    }
253
254    #[derive(Clone, Debug, Versionize)]
255    pub struct Test {
256        field_x: u64,
257        field0: u64,
258        field1: u32,
259        #[version(start = 2, default_fn = "field2_default")]
260        field2: u64,
261        #[version(
262            start = 3,
263            default_fn = "field3_default",
264            ser_fn = "field3_serialize",
265            de_fn = "field3_deserialize"
266        )]
267        field3: String,
268        #[version(
269            start = 4,
270            default_fn = "field4_default",
271            ser_fn = "field4_serialize",
272            de_fn = "field4_deserialize"
273        )]
274        field4: Vec<u64>,
275    }
276
277    impl Test {
278        fn field2_default(_: u16) -> u64 {
279            20
280        }
281        fn field3_default(_: u16) -> String {
282            "default".to_owned()
283        }
284        fn field4_default(_: u16) -> Vec<u64> {
285            vec![1, 2, 3, 4]
286        }
287        fn field4_serialize(&mut self, target_version: u16) -> VersionizeResult<()> {
288            // Fail if semantic serialization is called for the latest version.
289            assert_ne!(target_version, Test::version());
290            self.field0 = self.field4.iter().sum();
291
292            if self.field0 == 6666 {
293                return Err(versionize::VersionizeError::Semantic(
294                    "field4 element sum is 6666".to_owned(),
295                ));
296            }
297            Ok(())
298        }
299        fn field4_deserialize(&mut self, source_version: u16) -> VersionizeResult<()> {
300            // Fail if semantic deserialization is called for the latest version.
301            assert_ne!(source_version, Test::version());
302            self.field4 = vec![self.field0; 4];
303            Ok(())
304        }
305
306        fn field3_serialize(&mut self, target_version: u16) -> VersionizeResult<()> {
307            // Fail if semantic serialization is called for the previous versions only.
308            assert!(target_version < 3);
309            self.field_x += 1;
310            Ok(())
311        }
312
313        fn field3_deserialize(&mut self, source_version: u16) -> VersionizeResult<()> {
314            // Fail if semantic deserialization is called for the latest version.
315            assert!(source_version < 3);
316            self.field_x += 1;
317            if self.field0 == 7777 {
318                return Err(versionize::VersionizeError::Semantic(
319                    "field0 is 7777".to_owned(),
320                ));
321            }
322            Ok(())
323        }
324    }
325
326    #[test]
327    fn test_get_format_version() {
328        // Check if `get_format_version()` returns indeed the format
329        // version (the least significant 2 bytes) if the id is valid
330        // (the other bytes == BASE_MAGIC_ID).
331        #[cfg(target_arch = "x86_64")]
332        let good_magic_id = 0x0710_1984_8664_0001u64;
333        #[cfg(target_arch = "aarch64")]
334        let good_magic_id = 0x0710_1984_AAAA_0001u64;
335        #[cfg(target_arch = "powerpc64")]
336        let good_magic_id = 0x0710_1984_CC64_0001u64;
337        #[cfg(target_arch = "riscv64")]
338        let good_magic_id = 0x0710_1984_C564_0001u64;
339
340        assert_eq!(get_format_version(good_magic_id).unwrap(), 1u16);
341
342        // Flip a bit to invalidate the arch id.
343        let invalid_magic_id = good_magic_id | (1u64 << 63);
344        assert_eq!(
345            get_format_version(invalid_magic_id).unwrap_err(),
346            Error::InvalidMagic(invalid_magic_id)
347        );
348    }
349
350    #[test]
351    fn test_struct_semantic_fn() {
352        let mut vm = VersionMap::new();
353        vm.new_version()
354            .set_type_version(Test::type_id(), 2)
355            .new_version()
356            .set_type_version(Test::type_id(), 3)
357            .new_version()
358            .set_type_version(Test::type_id(), 4);
359        let state = Test {
360            field0: 0,
361            field1: 1,
362            field2: 2,
363            field3: "test".to_owned(),
364            field4: vec![4, 3, 2, 1],
365            field_x: 0,
366        };
367
368        let mut snapshot_mem = vec![0u8; 1024];
369
370        // Serialize as v1.
371        let mut snapshot = Snapshot::new(vm.clone(), 1);
372        snapshot
373            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state)
374            .unwrap();
375
376        let (mut restored_state, _) =
377            Snapshot::unchecked_load::<_, Test>(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
378
379        // The semantic serializer fn for field4 will set field0 to field4.iter().sum() == 10.
380        assert_eq!(restored_state.field0, state.field4.iter().sum::<u64>());
381        // The semantic deserializer for field4 will change field's value to vec![field0; 4].
382        assert_eq!(restored_state.field4, vec![restored_state.field0; 4]);
383        // The semantic serializer and deserializer for field3 will both increment field_x value.
384        assert_eq!(restored_state.field_x, 2);
385        // field1 should have the original value.
386        assert_eq!(restored_state.field1, 1);
387        // field2 should have the default value as this field was added at version 2.
388        assert_eq!(restored_state.field2, 20);
389
390        // Serialize as v3.
391        let mut snapshot = Snapshot::new(vm.clone(), 3);
392        snapshot
393            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state)
394            .unwrap();
395
396        (restored_state, _) =
397            Snapshot::unchecked_load::<_, Test>(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
398
399        // We expect only the semantic serializer and deserializer for field4 to be called at
400        // version 3. The semantic serializer will set field0 to field4.iter().sum() == 10.
401        assert_eq!(restored_state.field0, state.field4.iter().sum::<u64>());
402        // The semantic deserializer will create a 4 elements vec with all values == field0.
403        assert_eq!(restored_state.field4, vec![restored_state.field0; 4]);
404        // The semantic fn for field3 must not be called at version 3.
405        assert_eq!(restored_state.field_x, 0);
406
407        // Serialize as v4.
408        snapshot = Snapshot::new(vm.clone(), 4);
409        snapshot
410            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state)
411            .unwrap();
412
413        (restored_state, _) =
414            Snapshot::unchecked_load::<_, Test>(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
415
416        // The 4 semantic fns must not be called at version 4.
417        assert_eq!(restored_state.field0, 0);
418        assert_eq!(restored_state.field4, vec![4, 3, 2, 1]);
419
420        // Test error propagation from `versionize` crate.
421        // Load operation should fail if we don't use the whole `snapshot_mem` resulted from
422        // serialization.
423        snapshot_mem.truncate(10);
424        let restored_state_result: Result<(Test, _), Error> =
425            Snapshot::unchecked_load(&mut snapshot_mem.as_slice(), vm);
426
427        assert_eq!(
428            restored_state_result.unwrap_err(),
429            Error::Versionize(versionize::VersionizeError::Deserialize(String::from(
430                "Io(Error { kind: UnexpectedEof, message: \"failed to fill whole buffer\" })"
431            )))
432        );
433    }
434
435    #[test]
436    fn test_struct_default_fn() {
437        let mut vm = VersionMap::new();
438        vm.new_version()
439            .set_type_version(Test::type_id(), 2)
440            .new_version()
441            .set_type_version(Test::type_id(), 3)
442            .new_version()
443            .set_type_version(Test::type_id(), 4);
444        let state = Test {
445            field0: 0,
446            field1: 1,
447            field2: 2,
448            field3: "test".to_owned(),
449            field4: vec![4, 3, 2, 1],
450            field_x: 0,
451        };
452
453        let state_1 = Test1 {
454            field_x: 0,
455            field0: 0,
456            field1: 1,
457        };
458
459        let mut snapshot_mem = vec![0u8; 1024];
460
461        // Serialize as v1.
462        let mut snapshot = Snapshot::new(vm.clone(), 1);
463        snapshot
464            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state_1)
465            .unwrap();
466
467        let (mut restored_state, _) =
468            Snapshot::unchecked_load::<_, Test>(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
469        assert_eq!(restored_state.field1, state_1.field1);
470        assert_eq!(restored_state.field2, 20);
471        assert_eq!(restored_state.field3, "default");
472
473        // Serialize as v2.
474        snapshot = Snapshot::new(vm.clone(), 2);
475        snapshot
476            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state)
477            .unwrap();
478
479        (restored_state, _) =
480            Snapshot::unchecked_load::<_, Test>(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
481        assert_eq!(restored_state.field1, state.field1);
482        assert_eq!(restored_state.field2, 2);
483        assert_eq!(restored_state.field3, "default");
484
485        // Serialize as v3.
486        snapshot = Snapshot::new(vm.clone(), 3);
487        snapshot
488            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state)
489            .unwrap();
490
491        (restored_state, _) =
492            Snapshot::unchecked_load::<_, Test>(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
493        assert_eq!(restored_state.field1, state.field1);
494        assert_eq!(restored_state.field2, 2);
495        assert_eq!(restored_state.field3, "test");
496
497        // Serialize as v4.
498        snapshot = Snapshot::new(vm.clone(), 4);
499        snapshot
500            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state)
501            .unwrap();
502
503        (restored_state, _) =
504            Snapshot::unchecked_load::<_, Test>(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
505        assert_eq!(restored_state.field1, state.field1);
506        assert_eq!(restored_state.field2, 2);
507        assert_eq!(restored_state.field3, "test");
508    }
509
510    #[test]
511    fn test_crc_ok() {
512        let vm = VersionMap::new();
513        let state_1 = Test1 {
514            field_x: 0,
515            field0: 0,
516            field1: 1,
517        };
518
519        let mut snapshot_mem = vec![0u8; 1024];
520
521        // Serialize as v1.
522        let mut snapshot = Snapshot::new(vm.clone(), 1);
523        snapshot
524            .save(&mut snapshot_mem.as_mut_slice(), &state_1)
525            .unwrap();
526
527        let _ = Snapshot::load::<_, Test1>(&mut snapshot_mem.as_slice(), 38, vm).unwrap();
528    }
529
530    #[test]
531    fn test_invalid_snapshot_size() {
532        let vm = VersionMap::new();
533        // Create a snapshot shorter than CRC length.
534        let snapshot_mem = vec![0u8; 4];
535        let expected_err = Error::InvalidSnapshotSize;
536        let load_result: Result<(Test1, _), Error> =
537            Snapshot::load(&mut snapshot_mem.as_slice(), 4, vm);
538        assert_eq!(load_result.unwrap_err(), expected_err);
539    }
540
541    #[test]
542    fn test_corrupted_snapshot() {
543        let vm = VersionMap::new();
544        let state_1 = Test1 {
545            field_x: 0,
546            field0: 0,
547            field1: 1,
548        };
549
550        let mut snapshot_mem = vec![0u8; 1024];
551
552        // Serialize as v1.
553        let mut snapshot = Snapshot::new(vm.clone(), 1);
554        snapshot
555            .save(&mut snapshot_mem.as_mut_slice(), &state_1)
556            .unwrap();
557        snapshot_mem[20] = 123;
558
559        #[cfg(target_arch = "aarch64")]
560        let expected_err = Error::Crc64(0x1960_4E6A_A13F_6615);
561        #[cfg(target_arch = "x86_64")]
562        let expected_err = Error::Crc64(0x103F_8F52_8F51_20B1);
563        #[cfg(target_arch = "powerpc64")]
564        let expected_err = Error::Crc64(0x33D0_CCE5_DA3C_CCEA);
565        #[cfg(target_arch = "riscv64")]
566        let expected_err = Error::Crc64(0xFAC5_E225_5586_9011);
567
568        let load_result: Result<(Test1, _), Error> =
569            Snapshot::load(&mut snapshot_mem.as_slice(), 38, vm);
570        assert_eq!(load_result.unwrap_err(), expected_err);
571    }
572
573    #[allow(non_upper_case_globals)]
574    #[allow(non_camel_case_types)]
575    #[allow(non_snake_case)]
576    #[test]
577    fn test_kvm_bindings_struct() {
578        #[repr(C)]
579        #[derive(Debug, PartialEq, Eq, Versionize)]
580        pub struct kvm_pit_config {
581            pub flags: ::std::os::raw::c_uint,
582            pub pad: [::std::os::raw::c_uint; 15usize],
583        }
584
585        let state = kvm_pit_config {
586            flags: 123_456,
587            pad: [0; 15usize],
588        };
589
590        let vm = VersionMap::new();
591        let mut snapshot_mem = vec![0u8; 1024];
592        // Serialize as v1.
593        let mut snapshot = Snapshot::new(vm.clone(), 1);
594        snapshot
595            .save_without_crc(&mut snapshot_mem.as_mut_slice(), &state)
596            .unwrap();
597
598        let (restored_state, _) =
599            Snapshot::unchecked_load::<_, kvm_pit_config>(&mut snapshot_mem.as_slice(), vm)
600                .unwrap();
601        assert_eq!(restored_state, state);
602    }
603}