use std::future::Future; use std::pin::Pin; use std::sync::Arc; use anyhow::{bail, Error}; use super::crypt_config::{CryptConfig, CryptMode}; use super::data_blob::DataBlob; use super::datastore::DataStore; /// The ReadChunk trait allows reading backup data chunks (local or remote) pub trait ReadChunk { /// Returns the encoded chunk data fn read_raw_chunk(&self, digest: &[u8; 32]) -> Result; /// Returns the decoded chunk data fn read_chunk(&self, digest: &[u8; 32]) -> Result, Error>; } #[derive(Clone)] pub struct LocalChunkReader { store: Arc, crypt_config: Option>, crypt_mode: CryptMode, } impl LocalChunkReader { pub fn new(store: Arc, crypt_config: Option>, crypt_mode: CryptMode) -> Self { Self { store, crypt_config, crypt_mode, } } fn ensure_crypt_mode(&self, chunk_mode: CryptMode) -> Result<(), Error> { match self.crypt_mode { CryptMode::Encrypt => { match chunk_mode { CryptMode::Encrypt => Ok(()), CryptMode::SignOnly | CryptMode::None => bail!("Index and chunk CryptMode don't match."), } }, CryptMode::SignOnly | CryptMode::None => { match chunk_mode { CryptMode::Encrypt => bail!("Index and chunk CryptMode don't match."), CryptMode::SignOnly | CryptMode::None => Ok(()), } }, } } } impl ReadChunk for LocalChunkReader { fn read_raw_chunk(&self, digest: &[u8; 32]) -> Result { let chunk = self.store.load_chunk(digest)?; self.ensure_crypt_mode(chunk.crypt_mode()?)?; Ok(chunk) } fn read_chunk(&self, digest: &[u8; 32]) -> Result, Error> { let chunk = ReadChunk::read_raw_chunk(self, digest)?; let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref), Some(digest))?; Ok(raw_data) } } pub trait AsyncReadChunk: Send { /// Returns the encoded chunk data fn read_raw_chunk<'a>( &'a self, digest: &'a [u8; 32], ) -> Pin> + Send + 'a>>; /// Returns the decoded chunk data fn read_chunk<'a>( &'a self, digest: &'a [u8; 32], ) -> Pin, Error>> + Send + 'a>>; } impl AsyncReadChunk for LocalChunkReader { fn read_raw_chunk<'a>( &'a self, digest: &'a [u8; 32], ) -> Pin> + Send + 'a>> { Box::pin(async move{ let (path, _) = self.store.chunk_path(digest); let raw_data = tokio::fs::read(&path).await?; let chunk = DataBlob::load_from_reader(&mut &raw_data[..])?; self.ensure_crypt_mode(chunk.crypt_mode()?)?; Ok(chunk) }) } fn read_chunk<'a>( &'a self, digest: &'a [u8; 32], ) -> Pin, Error>> + Send + 'a>> { Box::pin(async move { let chunk = AsyncReadChunk::read_raw_chunk(self, digest).await?; let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref), Some(digest))?; // fixme: verify digest? Ok(raw_data) }) } }