summaryrefslogtreecommitdiff
path: root/src/chunk.rs
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2022-03-21 09:12:49 +0200
committerLars Wirzenius <liw@liw.fi>2022-03-22 19:18:35 +0200
commit48139725676fcce89a70897546969623f2474693 (patch)
treef9c00d2fcd3b7c597fd5062b90ef2279eb9db767 /src/chunk.rs
parent4c94c794ec805cf643826973e4f83826a1231e54 (diff)
downloadobnam2-48139725676fcce89a70897546969623f2474693.tar.gz
feat! store list of generations in a "client trust root" chunk
Backups made with this version can't be restored with old clients, and vice version. Sponsored-by: author
Diffstat (limited to 'src/chunk.rs')
-rw-r--r--src/chunk.rs95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/chunk.rs b/src/chunk.rs
index a37aa57..238bd01 100644
--- a/src/chunk.rs
+++ b/src/chunk.rs
@@ -102,3 +102,98 @@ impl GenerationChunk {
Ok(DataChunk::new(bytes, meta))
}
}
+
+/// A client trust root chunk.
+///
+/// This chunk contains all per-client backup information. As long as
+/// this chunk can be trusted, everything it links to can also be
+/// trusted, thanks to cryptographic signatures.
+#[derive(Debug, Serialize, Deserialize)]
+pub struct ClientTrust {
+ client_name: String,
+ previous_version: Option<ChunkId>,
+ timestamp: String,
+ backups: Vec<ChunkId>,
+}
+
+/// All the errors that may be returned for `ClientTrust` operations.
+#[derive(Debug, thiserror::Error)]
+pub enum ClientTrustError {
+ /// Error converting text from UTF8.
+ #[error(transparent)]
+ Utf8Error(#[from] std::str::Utf8Error),
+
+ /// Error parsing JSON as chunk metadata.
+ #[error("failed to parse JSON: {0}")]
+ JsonParse(serde_json::Error),
+
+ /// Error generating JSON from chunk metadata.
+ #[error("failed to serialize to JSON: {0}")]
+ JsonGenerate(serde_json::Error),
+}
+
+impl ClientTrust {
+ /// Create a new ClientTrust object.
+ pub fn new(
+ name: &str,
+ previous_version: Option<ChunkId>,
+ timestamp: String,
+ backups: Vec<ChunkId>,
+ ) -> Self {
+ Self {
+ client_name: name.to_string(),
+ previous_version,
+ timestamp,
+ backups,
+ }
+ }
+
+ /// Return client name.
+ pub fn client_name(&self) -> &str {
+ &self.client_name
+ }
+
+ /// Return id of previous version, if any.
+ pub fn previous_version(&self) -> Option<ChunkId> {
+ self.previous_version.clone()
+ }
+
+ /// Return timestamp.
+ pub fn timestamp(&self) -> &str {
+ &self.timestamp
+ }
+
+ /// Return list of all backup generations known.
+ pub fn backups(&self) -> &[ChunkId] {
+ &self.backups
+ }
+
+ /// Append a backup generation to the list.
+ pub fn append_backup(&mut self, id: &ChunkId) {
+ self.backups.push(id.clone());
+ }
+
+ /// Update for new upload.
+ ///
+ /// This needs to happen every time the chunk is updated so that
+ /// the timestamp gets updated.
+ pub fn finalize(&mut self, timestamp: String) {
+ self.timestamp = timestamp;
+ }
+
+ /// Convert generation chunk to a data chunk.
+ pub fn to_data_chunk(&self) -> Result<DataChunk, ClientTrustError> {
+ let json: String = serde_json::to_string(self).map_err(ClientTrustError::JsonGenerate)?;
+ let bytes = json.as_bytes().to_vec();
+ let checksum = Checksum::sha256_from_str_unchecked("client-trust");
+ let meta = ChunkMeta::new_generation(&checksum, "");
+ Ok(DataChunk::new(bytes, meta))
+ }
+
+ /// Create a new ClientTrust from a data chunk.
+ pub fn from_data_chunk(chunk: &DataChunk) -> Result<Self, ClientTrustError> {
+ let data = chunk.data();
+ let data = std::str::from_utf8(data)?;
+ serde_json::from_str(data).map_err(ClientTrustError::JsonParse)
+ }
+}