trussed 0.2.0-rc.1

Modern Cryptographic Firmware
Documentation
use std::{
    fs::{File, OpenOptions},
    io::{Read as _, Seek as _, SeekFrom, Write as _},
    path::PathBuf,
};

use generic_array::typenum::{U512, U8};
use littlefs2::{const_ram_storage, driver::Storage, object_safe::DynStorageAlloc};
use littlefs2_core::{DynFilesystem, Error, Result};

use crate::store;

pub struct StoreConfig<'a> {
    pub internal: StorageConfig<'a>,
    pub external: StorageConfig<'a>,
    pub volatile: StorageConfig<'a>,
}

impl StoreConfig<'_> {
    pub fn ram() -> Self {
        Self {
            internal: StorageConfig::ram(),
            external: StorageConfig::ram(),
            volatile: StorageConfig::ram(),
        }
    }

    pub fn with_store<F, T>(&mut self, f: F) -> T
    where
        F: FnOnce(Store) -> T,
    {
        self.internal
            .with_fs(|internal| {
                self.external
                    .with_fs(|external| {
                        self.volatile
                            .with_fs(|volatile| {
                                let store = Store {
                                    internal,
                                    external,
                                    volatile,
                                };
                                f(store)
                            })
                            .expect("failed to mount volatile storage")
                    })
                    .expect("failed to mount external storage")
            })
            .expect("failed to mount internal storage")
    }
}

pub struct StorageConfig<'a> {
    pub storage: Box<dyn DynStorageAlloc + 'a>,
    pub format: bool,
}

impl StorageConfig<'_> {
    pub fn ram() -> Self {
        Self {
            storage: Box::new(RamStorage::new()),
            format: true,
        }
    }

    pub fn filesystem(path: PathBuf) -> Self {
        let len = u64::try_from(STORAGE_SIZE).unwrap();
        let format = if let Ok(file) = File::open(&path) {
            assert_eq!(file.metadata().unwrap().len(), len);
            false
        } else {
            let file = File::create(&path).expect("failed to create storage file");
            file.set_len(len).expect("failed to set storage file len");
            true
        };
        Self {
            storage: Box::new(FilesystemStorage(path)),
            format,
        }
    }

    pub fn with_fs<F, T>(&mut self, f: F) -> Result<T>
    where
        F: FnOnce(&dyn DynFilesystem) -> T,
    {
        if self.format {
            self.storage.format()?;
        }
        self.storage.mount_and_then_once(|fs| Ok(f(fs)))
    }
}

const STORAGE_SIZE: usize = 512 * 128;

const_ram_storage!(RamStorage, STORAGE_SIZE);

struct FilesystemStorage(PathBuf);

impl Storage for FilesystemStorage {
    const READ_SIZE: usize = 16;
    const WRITE_SIZE: usize = 16;
    const BLOCK_SIZE: usize = 512;

    const BLOCK_COUNT: usize = 128;
    const BLOCK_CYCLES: isize = -1;

    type CACHE_SIZE = U512;
    type LOOKAHEAD_SIZE = U8;

    fn read(&mut self, offset: usize, buffer: &mut [u8]) -> Result<usize> {
        debug!("read: offset: {}, len: {}", offset, buffer.len());
        let mut file = File::open(&self.0).unwrap();
        file.seek(SeekFrom::Start(offset as _)).unwrap();
        let bytes_read = file.read(buffer).unwrap();
        assert!(bytes_read <= buffer.len());
        Ok(bytes_read as _)
    }

    fn write(&mut self, offset: usize, data: &[u8]) -> Result<usize> {
        debug!("write: offset: {}, len: {}", offset, data.len());
        if offset + data.len() > STORAGE_SIZE {
            return Err(Error::NO_SPACE);
        }
        let mut file = OpenOptions::new().write(true).open(&self.0).unwrap();
        file.seek(SeekFrom::Start(offset as _)).unwrap();
        let bytes_written = file.write(data).unwrap();
        assert_eq!(bytes_written, data.len());
        file.flush().unwrap();
        Ok(bytes_written)
    }

    fn erase(&mut self, offset: usize, len: usize) -> Result<usize> {
        debug!("erase: offset: {}, len: {}", offset, len);
        if offset + len > STORAGE_SIZE {
            return Err(Error::NO_SPACE);
        }
        let mut file = OpenOptions::new().write(true).open(&self.0).unwrap();
        file.seek(SeekFrom::Start(offset as _)).unwrap();
        let zero_block = [0xFFu8; Self::BLOCK_SIZE];
        for _ in 0..(len / Self::BLOCK_SIZE) {
            let bytes_written = file.write(&zero_block).unwrap();
            assert_eq!(bytes_written, Self::BLOCK_SIZE);
        }
        file.flush().unwrap();
        Ok(len)
    }
}

#[derive(Clone, Copy)]
pub struct Store<'a> {
    pub internal: &'a dyn DynFilesystem,
    pub external: &'a dyn DynFilesystem,
    pub volatile: &'a dyn DynFilesystem,
}

impl store::Store for Store<'_> {
    fn ifs(&self) -> &dyn DynFilesystem {
        self.internal
    }

    fn efs(&self) -> &dyn DynFilesystem {
        self.external
    }

    fn vfs(&self) -> &dyn DynFilesystem {
        self.volatile
    }
}