sirenia: Add FileStorage and persistence.

This adds the FileStorage implementation of the Storage trait and the
communication::persistence module. It also moves the storage module from
the communication module to the crate:: level, and coverts the errors
defined in the storage module over to use "thiserror".

BUG=b:173598376, b:173128950
TEST=cargo test --workspace

Cq-Depend: chromium:2595852
Change-Id: Id28fcfed7a85dcd3dc6f425216b326162aeb1088
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2596384
Reviewed-by: Nicole Anderson-Au <nvaa@google.com>
Commit-Queue: Allen Webb <allenwebb@google.com>
Tested-by: Allen Webb <allenwebb@google.com>
diff --git a/sirenia/libsirenia/Cargo.toml b/sirenia/libsirenia/Cargo.toml
index 94c85f3..5b36cbc 100644
--- a/sirenia/libsirenia/Cargo.toml
+++ b/sirenia/libsirenia/Cargo.toml
@@ -12,3 +12,4 @@
 minijail = { path = "../../../aosp/external/minijail/rust/minijail" } # provided by ebuild
 serde = { version = "1.0.114", features = ["derive"] }
 sys_util = { path = "../../../platform/crosvm/sys_util" } # provided by ebuild
+thiserror = "1.0.20"
diff --git a/sirenia/libsirenia/src/communication/mod.rs b/sirenia/libsirenia/src/communication/mod.rs
index f488f9b..488344c 100644
--- a/sirenia/libsirenia/src/communication/mod.rs
+++ b/sirenia/libsirenia/src/communication/mod.rs
@@ -7,7 +7,7 @@
 //! trichechus.
 //!
 
-pub mod storage;
+pub mod persistence;
 
 use std::fmt::{self, Debug, Display};
 use std::io::{self, BufWriter, Read, Write};
diff --git a/sirenia/libsirenia/src/communication/persistence.rs b/sirenia/libsirenia/src/communication/persistence.rs
new file mode 100644
index 0000000..5171557
--- /dev/null
+++ b/sirenia/libsirenia/src/communication/persistence.rs
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Defines messages used for communication between Trichechus and Cronista for storing and
+//! retrieving persistent data.
+
+use serde::{Deserialize, Serialize};
+
+use crate::storage::StorableMember;
+
+//TODO These messages also need to carry enough information to prove the entry was recorded in the
+//log.
+#[derive(Deserialize, Serialize)]
+pub enum Request {
+    Persist {
+        domain: String,
+        identifier: String,
+        data: StorableMember,
+    },
+    Retrieve {
+        domain: String,
+        identifier: String,
+    },
+}
+
+#[derive(Deserialize, Serialize)]
+pub enum Response {
+    Persist { status: i32 },
+    Retrieve { status: i32, data: StorableMember },
+}
diff --git a/sirenia/libsirenia/src/lib.rs b/sirenia/libsirenia/src/lib.rs
index bf112cb..09f824e 100644
--- a/sirenia/libsirenia/src/lib.rs
+++ b/sirenia/libsirenia/src/lib.rs
@@ -8,5 +8,6 @@
 pub mod communication;
 pub mod linux;
 pub mod sandbox;
+pub mod storage;
 pub mod to_sys_util;
 pub mod transport;
diff --git a/sirenia/libsirenia/src/storage/file_storage.rs b/sirenia/libsirenia/src/storage/file_storage.rs
new file mode 100644
index 0000000..504f5f1
--- /dev/null
+++ b/sirenia/libsirenia/src/storage/file_storage.rs
@@ -0,0 +1,186 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Implements file based implementation of the Storage trait.
+
+use std::fs::File;
+use std::io::{Error as IoError, ErrorKind, Read, Write};
+use std::path::{Path, PathBuf};
+use std::result::Result as StdResult;
+
+use flexbuffers::{from_slice, FlexbufferSerializer};
+
+use super::{to_read_data_error, to_write_data_error, Error, Result, Storable, Storage};
+
+pub struct FileStorage {
+    root: PathBuf,
+}
+
+/// Provides an implementation of the Storage trait that writes the entries to files at a specified
+/// path named by the id.
+impl FileStorage {
+    pub fn new(root: PathBuf) -> StdResult<Self, IoError> {
+        if !root.is_dir() {
+            return Err(IoError::from(ErrorKind::NotFound));
+        }
+        Ok(FileStorage { root })
+    }
+
+    /// Return true if this path component is allowed.
+    fn check_path_component(component: &str) -> bool {
+        component != "." && component != ".."
+    }
+
+    /// Validity checks on the id performed before touching the file-system.
+    fn validate_id(&self, id: &str) -> Result<PathBuf> {
+        let path = Path::new(id);
+
+        // Get first component.
+        let mut itr = path.components();
+        let first = PathBuf::from(
+            itr.next()
+                .ok_or_else(|| Error::InvalidIdForStorage(id.to_string()))?
+                .as_os_str(),
+        );
+
+        // If there is more than one component or the first component is a special path
+        if itr.next().is_some() || !FileStorage::check_path_component(&first.to_string_lossy()) {
+            return Err(Error::InvalidIdForStorage(id.to_string()));
+        }
+
+        Ok(self.root.join(first))
+    }
+}
+
+impl Storage for FileStorage {
+    fn read_data<S: Storable>(&mut self, id: &str) -> Result<S> {
+        let filepath = self.validate_id(id)?;
+
+        if !filepath.exists() {
+            return Err(Error::EmptyRead);
+        }
+
+        let mut contents: Vec<u8> = Vec::new();
+        File::open(filepath)
+            .map_err(to_read_data_error)?
+            .read_to_end(&mut contents)
+            .map_err(to_read_data_error)?;
+
+        from_slice(&contents).map_err(to_read_data_error)
+    }
+
+    fn write_data<S: Storable>(&mut self, id: &str, data: &S) -> Result<()> {
+        let filename = self.validate_id(id)?;
+        let mut destination = File::create(filename).map_err(to_write_data_error)?;
+
+        let mut ser = FlexbufferSerializer::new();
+        data.serialize(&mut ser).map_err(to_write_data_error)?;
+
+        destination
+            .write_all(&ser.take_buffer())
+            .map_err(to_write_data_error)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    use std::fs::create_dir;
+    use std::time::{SystemTime, UNIX_EPOCH};
+
+    use libchromeos::scoped_path::{get_temp_path, ScopedPath};
+
+    const VALID_TEST_ID: &str = "Test Data";
+
+    struct TestFileStorage {
+        storage: FileStorage,
+        /// This is needed for its Drop implementation.
+        #[allow(dead_code)]
+        storage_root: ScopedPath<PathBuf>,
+    }
+
+    impl TestFileStorage {
+        fn new() -> Self {
+            let storage_root = ScopedPath::create(get_temp_path(None).to_path_buf()).unwrap();
+            let storage = FileStorage::new(storage_root.to_path_buf()).unwrap();
+            TestFileStorage {
+                storage_root,
+                storage,
+            }
+        }
+    }
+
+    impl AsMut<FileStorage> for TestFileStorage {
+        fn as_mut(&mut self) -> &mut FileStorage {
+            &mut self.storage
+        }
+    }
+
+    fn get_test_value() -> u64 {
+        SystemTime::now()
+            .duration_since(UNIX_EPOCH)
+            .unwrap()
+            .as_secs()
+    }
+
+    #[test]
+    fn storage_new_success() {
+        TestFileStorage::new();
+    }
+
+    #[test]
+    fn storage_new_notexist() {
+        assert!(FileStorage::new(get_temp_path(None).to_path_buf()).is_err());
+    }
+
+    #[test]
+    fn storage_readwrite_success() {
+        let mut storage = TestFileStorage::new();
+
+        let test_value = get_test_value();
+
+        storage
+            .as_mut()
+            .write_data(VALID_TEST_ID, &test_value)
+            .unwrap();
+
+        let read_value: u64 = storage.as_mut().read_data(VALID_TEST_ID).unwrap();
+
+        assert_eq!(test_value, read_value);
+    }
+
+    #[test]
+    fn storage_read_emptyread() {
+        let mut storage = TestFileStorage::new();
+
+        assert!(matches!(
+            storage.as_mut().read_data::<u64>(VALID_TEST_ID),
+            Err(Error::EmptyRead)
+        ));
+    }
+
+    #[test]
+    fn storage_write_ioerror() {
+        let mut storage = TestFileStorage::new();
+
+        let path = storage.as_mut().validate_id(VALID_TEST_ID).unwrap();
+        create_dir(path).unwrap();
+
+        assert!(storage
+            .as_mut()
+            .write_data(VALID_TEST_ID, &get_test_value())
+            .is_err());
+    }
+
+    #[test]
+    fn storage_read_ioerror() {
+        let mut storage = TestFileStorage::new();
+
+        let path = storage.as_mut().validate_id(VALID_TEST_ID).unwrap();
+        create_dir(path).unwrap();
+
+        assert!(storage.as_mut().read_data::<u64>(VALID_TEST_ID).is_err());
+    }
+}
diff --git a/sirenia/libsirenia/src/communication/storage/mod.rs b/sirenia/libsirenia/src/storage/mod.rs
similarity index 61%
rename from sirenia/libsirenia/src/communication/storage/mod.rs
rename to sirenia/libsirenia/src/storage/mod.rs
index a15f217..a79921a 100644
--- a/sirenia/libsirenia/src/communication/storage/mod.rs
+++ b/sirenia/libsirenia/src/storage/mod.rs
@@ -4,39 +4,43 @@
 
 //! Defines the messages and abstracts out communication for storage.
 
-use std::any::Any;
-use std::fmt::{self, Debug, Display};
+mod file_storage;
+pub use file_storage::FileStorage;
+
+use std::any::{type_name, Any};
+use std::borrow::Borrow;
+use std::error::Error as StdError;
+use std::fmt::{self, Debug};
 use std::result::Result as StdResult;
 
 use flexbuffers::FlexbufferSerializer;
 use serde::de::{Deserialize, DeserializeOwned, Visitor};
 use serde::export::Formatter;
 use serde::{Deserializer, Serialize, Serializer};
-use std::borrow::Borrow;
+use thiserror::Error as ThisError;
 
-#[derive(Debug)]
+#[derive(ThisError, Debug)]
 pub enum Error {
-    /// Id does not exist in the store
+    #[error("id not found {0}")]
     IdNotFound(String),
-    /// Error casting data
-    CastData,
-    /// Error reading data from storage
-    ReadData,
-    /// Error writing data to storage
-    WriteData,
+    #[error("storage regjected id {0}")]
+    InvalidIdForStorage(String),
+    #[error("failed to cast data from '{from}' to '{to}'")]
+    CastData { from: String, to: String },
+    #[error("failed to read data")]
+    ReadData(Option<Box<dyn StdError>>),
+    #[error("id not written yet")]
+    EmptyRead,
+    #[error("failed to write data")]
+    WriteData(Option<Box<dyn StdError>>),
 }
 
-impl Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use self::Error::*;
+pub fn to_read_data_error<E: StdError + 'static>(err: E) -> Error {
+    Error::ReadData(Some(Box::new(err)))
+}
 
-        match self {
-            IdNotFound(e) => write!(f, "Id not found: {}", e),
-            CastData => write!(f, "Problem casting data"),
-            ReadData => write!(f, "Problem reading data from storage"),
-            WriteData => write!(f, "Problem writing data to storage"),
-        }
-    }
+pub fn to_write_data_error<E: StdError + 'static>(err: E) -> Error {
+    Error::WriteData(Some(Box::new(err)))
 }
 
 /// The result of an operation in this crate.
@@ -46,7 +50,6 @@
 impl<S: Any + Serialize + DeserializeOwned> Storable for S {}
 
 pub trait Storage {
-    fn new() -> Self;
     fn read_data<S: Storable>(&mut self, id: &str) -> Result<S>;
     fn write_data<S: Storable>(&mut self, id: &str, data: &S) -> Result<()>;
 }
@@ -57,11 +60,14 @@
     Deserialized {
         value: Box<dyn Any>,
         store: fn(&dyn Any) -> Result<Vec<u8>>,
+        typename: &'static str,
     },
     // TODO consider zero copy alternatives to Vec.
     Serialized(Vec<u8>),
 }
 
+const SERIALIZED: &str = "<unknown serialized>";
+
 impl StorableMember {
     pub fn new_serialized(data: Vec<u8>) -> Self {
         StorableMember::Serialized(data)
@@ -71,52 +77,91 @@
         StorableMember::Deserialized {
             value: Box::new(value),
             store: store::<S>,
+            typename: type_name::<Self>(),
+        }
+    }
+
+    fn get_typename(&self) -> &'static str {
+        match self {
+            StorableMember::Deserialized {
+                value: _,
+                store: _,
+                typename,
+            } => typename,
+            StorableMember::Serialized(_) => SERIALIZED,
         }
     }
 
     pub fn interpret<S: Storable>(&mut self) -> Result<&mut Self> {
         if let StorableMember::Serialized(data) = self {
-            let ser_reader = flexbuffers::Reader::get_root(&data).map_err(|_| Error::ReadData)?;
+            let ser_reader = flexbuffers::Reader::get_root(&data).map_err(to_read_data_error)?;
             *self = StorableMember::new_deserialized(
-                S::deserialize(ser_reader).map_err(|_| Error::ReadData)?,
+                S::deserialize(ser_reader).map_err(to_read_data_error)?,
             );
         }
         Ok(self)
     }
 
     pub fn try_borrow_mut<S: Storable>(&mut self) -> Result<&mut S> {
-        if let StorableMember::Deserialized { value, store: _ } = self.interpret::<S>()? {
-            if let Some(value) = value.downcast_mut::<S>() {
-                return Ok(value);
-            }
+        self.interpret::<S>()?;
+        match self {
+            StorableMember::Deserialized {
+                value,
+                store: _,
+                typename,
+            } => match value.downcast_mut::<S>() {
+                Some(value) => Ok(value),
+                None => Err(Error::CastData {
+                    from: typename.to_string(),
+                    to: type_name::<S>().to_string(),
+                }),
+            },
+            StorableMember::Serialized(_) => Err(Error::CastData {
+                from: self.get_typename().to_string(),
+                to: type_name::<S>().to_string(),
+            }),
         }
-        Err(Error::CastData)
     }
 
     pub fn try_borrow<S: Storable>(&self) -> Result<&S> {
-        if let StorableMember::Deserialized { value, store: _ } = self {
+        if let StorableMember::Deserialized {
+            value,
+            store: _,
+            typename: _,
+        } = self
+        {
             if let Some(value) = value.downcast_ref::<S>() {
                 return Ok(value);
             }
         }
-        Err(Error::CastData)
+        Err(Error::CastData {
+            from: self.get_typename().to_string(),
+            to: type_name::<S>().to_string(),
+        })
     }
 }
 
-fn store<S: Storable>(val: &dyn std::any::Any) -> Result<Vec<u8>> {
+fn store<S: Storable>(val: &dyn Any) -> Result<Vec<u8>> {
     if let Some(value) = val.downcast_ref::<S>() {
         let mut ser = FlexbufferSerializer::new();
-        value.serialize(&mut ser).map_err(|_| Error::WriteData)?;
+        value.serialize(&mut ser).map_err(to_write_data_error)?;
         Ok(ser.take_buffer())
     } else {
-        Err(Error::CastData)
+        Err(Error::CastData {
+            from: type_name::<dyn Any>().to_string(),
+            to: type_name::<S>().to_string(),
+        })
     }
 }
 
 impl Into<Vec<u8>> for StorableMember {
     fn into(self) -> Vec<u8> {
         match self {
-            StorableMember::Deserialized { value, store } => store(value.borrow()).unwrap(),
+            StorableMember::Deserialized {
+                value,
+                store,
+                typename: _,
+            } => store(value.borrow()).unwrap(),
             StorableMember::Serialized(value) => value,
         }
     }
@@ -126,7 +171,11 @@
     fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
         let data;
         serializer.serialize_bytes(match &self {
-            StorableMember::Deserialized { value, store } => {
+            StorableMember::Deserialized {
+                value,
+                store,
+                typename: _,
+            } => {
                 data = store(value.borrow()).unwrap();
                 &data
             }
diff --git a/sirenia/manatee-runtime/src/lib.rs b/sirenia/manatee-runtime/src/lib.rs
index f6c70a0..69ab7ac 100644
--- a/sirenia/manatee-runtime/src/lib.rs
+++ b/sirenia/manatee-runtime/src/lib.rs
@@ -8,7 +8,7 @@
 use std::collections::HashMap;
 use std::fmt::{self, Debug, Display};
 
-use libsirenia::communication::storage::{self, Storable, StorableMember, Storage};
+use libsirenia::storage::{self, Storable, StorableMember, Storage};
 
 #[derive(Debug)]
 pub enum Error {
diff --git a/sirenia/src/communication/mod.rs b/sirenia/src/communication/mod.rs
index 717445a..9ea3632 100644
--- a/sirenia/src/communication/mod.rs
+++ b/sirenia/src/communication/mod.rs
@@ -9,8 +9,6 @@
 
 use serde::{Deserialize, Serialize};
 
-pub mod storage;
-
 #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
 pub enum Request {
     StartSession(AppInfo), // TODO: Add source port
diff --git a/sirenia/src/lib.rs b/sirenia/src/lib.rs
index c73bc19..0231be8 100644
--- a/sirenia/src/lib.rs
+++ b/sirenia/src/lib.rs
@@ -8,5 +8,6 @@
 pub mod build_info; // This is generated by build.rs.
 pub mod cli;
 pub mod communication;
+pub mod storage;
 
 include!("bindings/include_modules.rs");
diff --git a/sirenia/src/communication/storage/mod.rs b/sirenia/src/storage/mod.rs
similarity index 81%
rename from sirenia/src/communication/storage/mod.rs
rename to sirenia/src/storage/mod.rs
index fab6cfb..5c6add3 100644
--- a/sirenia/src/communication/storage/mod.rs
+++ b/sirenia/src/storage/mod.rs
@@ -5,7 +5,7 @@
 //! Defines the messages and abstracts out communication for storage between
 //! TEE apps, Trichechus, and Dugong.
 
-use libsirenia::communication::storage::{self, Error, Result, Storable, Storage};
+use libsirenia::storage::{Error, Result, Storable, Storage};
 use libsirenia::transport::Transport;
 
 pub struct TrichechusStorage {
@@ -13,16 +13,18 @@
     connection: Transport,
 }
 
-impl Storage for TrichechusStorage {
+impl TrichechusStorage {
     fn new() -> Self {
         panic!()
     }
+}
 
+impl Storage for TrichechusStorage {
     fn read_data<S: Storable>(&mut self, id: &str) -> Result<S> {
-        Err(Error::ReadData)
+        Err(Error::ReadData(None))
     }
 
     fn write_data<S: Storable>(&mut self, id: &str, data: &S) -> Result<()> {
-        Err(Error::WriteData)
+        Err(Error::WriteData(None))
     }
 }