fxfs/
checksum.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::errors::FxfsError;
6use anyhow::Error;
7use byteorder::{ByteOrder, LittleEndian};
8use fprint::TypeFingerprint;
9use serde::{Deserialize, Serialize};
10use static_assertions::assert_cfg;
11use zerocopy::{FromBytes as _, IntoBytes as _};
12
13/// For the foreseeable future, Fxfs will use 64-bit checksums.
14pub type Checksum = u64;
15
16/// Generates a Fletcher64 checksum of |buf| seeded by |previous|.
17///
18/// All logfile blocks are covered by a fletcher64 checksum as the last 8 bytes in a block.
19///
20/// We also use this checksum for integrity validation of potentially out-of-order writes
21/// during Journal replay.
22pub fn fletcher64(buf: &[u8], previous: Checksum) -> Checksum {
23    assert!(buf.len() % 4 == 0);
24    let mut lo = previous as u32;
25    let mut hi = (previous >> 32) as u32;
26    for chunk in buf.chunks(4) {
27        lo = lo.wrapping_add(LittleEndian::read_u32(chunk));
28        hi = hi.wrapping_add(lo);
29    }
30    (hi as u64) << 32 | lo as u64
31}
32
33/// A vector of fletcher64 checksums, one per block.
34/// These are stored as a flat array of bytes for efficient deserialization.
35pub type Checksums = ChecksumsV38;
36
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TypeFingerprint)]
38#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
39pub struct ChecksumsV38 {
40    sums: Vec<u8>,
41}
42
43impl Checksums {
44    pub fn fletcher(checksums: Vec<Checksum>) -> Self {
45        assert_cfg!(target_endian = "little");
46        let checksums_as_u8: &[u8] = &*checksums.as_bytes();
47        Self { sums: checksums_as_u8.to_owned() }
48    }
49
50    pub fn len(&self) -> usize {
51        self.sums.len() / std::mem::size_of::<Checksum>()
52    }
53
54    pub fn maybe_as_ref(&self) -> Result<&[Checksum], Error> {
55        assert_cfg!(target_endian = "little");
56        <[Checksum]>::ref_from_bytes(&self.sums).map_err(|_| FxfsError::Inconsistent.into())
57    }
58
59    pub fn offset_by(&self, amount: usize) -> Self {
60        Checksums { sums: self.sums[amount * std::mem::size_of::<Checksum>()..].to_vec() }
61    }
62
63    pub fn shrunk(&self, len: usize) -> Self {
64        Checksums { sums: self.sums[..len * std::mem::size_of::<Checksum>()].to_vec() }
65    }
66}
67
68#[derive(Debug, Serialize, Deserialize, TypeFingerprint)]
69pub enum ChecksumsV37 {
70    None,
71    Fletcher(Vec<u8>),
72}
73
74impl ChecksumsV37 {
75    pub fn fletcher(checksums: Vec<Checksum>) -> Self {
76        assert_cfg!(target_endian = "little");
77        let checksums_as_u8: &[u8] = &*checksums.as_bytes();
78        Self::Fletcher(checksums_as_u8.to_owned())
79    }
80
81    pub fn migrate(self) -> Option<Checksums> {
82        match self {
83            Self::None => None,
84            Self::Fletcher(sums) => Some(Checksums { sums }),
85        }
86    }
87}
88
89#[derive(Debug, Serialize, Deserialize, TypeFingerprint)]
90pub enum ChecksumsV32 {
91    None,
92    Fletcher(Vec<u64>),
93}
94
95impl From<ChecksumsV32> for ChecksumsV37 {
96    fn from(checksums: ChecksumsV32) -> Self {
97        match checksums {
98            ChecksumsV32::None => Self::None,
99            ChecksumsV32::Fletcher(sums) => Self::fletcher(sums),
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use crate::checksum::Checksums;
107    use crate::errors::FxfsError;
108
109    #[test]
110    fn checksum_encoding_idempotent() {
111        let mut checksums = vec![0xabu64 << 56, 0x11002200u64, u64::MAX, 0];
112        checksums.reserve_exact(5);
113
114        let encoded = Checksums::fletcher(checksums.clone());
115        let decoded = encoded.maybe_as_ref().unwrap();
116
117        assert_eq!(decoded, &checksums[..]);
118    }
119
120    #[test]
121    fn deserialize_invalid_checksum() {
122        let bad = Checksums { sums: vec![0, 1, 2, 3, 4, 5, 6] };
123        let res = bad.maybe_as_ref().expect_err("deserialization should fail");
124        assert!(FxfsError::Inconsistent.matches(&res));
125    }
126}