blob: f6c70a0ba5bd9705933a75aabc8cb04e0cb90cdf [file] [log] [blame]
// 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.
//! API endpoint library for the TEE apps to communicate with Trichechus.
use std::borrow::{Borrow, BorrowMut};
use std::collections::HashMap;
use std::fmt::{self, Debug, Display};
use libsirenia::communication::storage::{self, Storable, StorableMember, Storage};
#[derive(Debug)]
pub enum Error {
/// Error reading data from storage
ReadData(storage::Error),
/// Error writing data to storage
WriteData(storage::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
ReadData(e) => write!(f, "Problem reading data from storage: {}", e),
WriteData(e) => write!(f, "Problem writing data to storage: {}", e),
}
}
}
/// The result of an operation in this crate.
pub type Result<T> = std::result::Result<T, storage::Error>;
/// Represents some scoped data temporarily loaded from the backing store.
pub struct ScopedData<S: Storable, T: Storage> {
identifier: String,
data: S,
storage: T,
}
impl<S: Storable, T: Storage> AsRef<S> for ScopedData<S, T> {
fn as_ref(&self) -> &S {
self.borrow()
}
}
impl<S: Storable, T: Storage> AsMut<S> for ScopedData<S, T> {
fn as_mut(&mut self) -> &mut S {
self.borrow_mut()
}
}
/// Borrows the data read-only.
impl<S: Storable, T: Storage> Borrow<S> for ScopedData<S, T> {
fn borrow(&self) -> &S {
&self.data
}
}
/// Borrows a mutable reference to the data (the ScopedData must be
/// constructed read-write).
impl<S: Storable, T: Storage> BorrowMut<S> for ScopedData<S, T> {
fn borrow_mut(&mut self) -> &mut S {
&mut self.data
}
}
impl<S: Storable, T: Storage> Drop for ScopedData<S, T> {
fn drop(&mut self) {
// TODO: Figure out how we want to handle errors on storing.
// We might want to log failures, but not necessarily crash. This will
// require us to bind mount the log into the sandbox though
// (which we should probably do anyway).
//
// One option would be set a callback. We could provide some standard
// callbacks like unwrap and log.
self.storage
.write_data(&self.identifier, &self.data)
.unwrap();
}
}
impl<S: Storable, T: Storage> ScopedData<S, T> {
/// Reads the data into itself then writes back on a flush.
pub fn new(mut storage: T, identifier: &str, f: fn(&str) -> S) -> Result<Self> {
match storage.read_data(identifier) {
Ok(data) => {
let id = identifier.to_string();
Ok(ScopedData {
identifier: id,
data,
storage,
})
}
Err(storage::Error::IdNotFound(_)) => {
let data = f(identifier);
let id = identifier.to_string();
Ok(ScopedData {
identifier: id,
data,
storage,
})
}
Err(e) => Err(e),
}
}
/// Write the data back out to the backing store.
pub fn flush(&mut self) -> Result<()> {
self.storage
.write_data(&self.identifier, &self.data)
.unwrap();
Ok(())
}
/// Drop the changes without committing them.
pub fn abandon(&mut self) -> Result<()> {
self.data = self.storage.read_data(&self.identifier).unwrap();
Ok(())
}
}
/// Represents an entire key value store for one identifier.
pub type ScopedKeyValueStore<T> = ScopedData<HashMap<String, StorableMember>, T>;