summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/obnam.rs7
-rw-r--r--src/chunk.rs6
-rw-r--r--src/chunkid.rs8
-rw-r--r--src/client.rs35
-rw-r--r--src/cmd/get_chunk.rs15
-rw-r--r--src/cmd/mod.rs3
-rw-r--r--src/error.rs6
7 files changed, 63 insertions, 17 deletions
diff --git a/src/bin/obnam.rs b/src/bin/obnam.rs
index f31884b..e5703ed 100644
--- a/src/bin/obnam.rs
+++ b/src/bin/obnam.rs
@@ -2,7 +2,7 @@ use log::{debug, error, info, LevelFilter};
use log4rs::append::file::FileAppender;
use log4rs::config::{Appender, Config, Logger, Root};
use obnam::client::ClientConfig;
-use obnam::cmd::{backup, list, list_files, restore};
+use obnam::cmd::{backup, get_chunk, list, list_files, restore};
use std::path::{Path, PathBuf};
use structopt::StructOpt;
@@ -23,6 +23,7 @@ fn main() -> anyhow::Result<()> {
Command::List => list(&config),
Command::ListFiles { gen_id } => list_files(&config, &gen_id),
Command::Restore { gen_id, to } => restore(&config, &gen_id, &to),
+ Command::GetChunk { chunk_id } => get_chunk(&config, &chunk_id),
};
if let Err(ref e) = result {
@@ -60,6 +61,10 @@ enum Command {
#[structopt(parse(from_os_str))]
to: PathBuf,
},
+ GetChunk {
+ #[structopt()]
+ chunk_id: String,
+ },
}
fn setup_logging(filename: &Path) -> anyhow::Result<()> {
diff --git a/src/chunk.rs b/src/chunk.rs
index 7fdeccb..4917b60 100644
--- a/src/chunk.rs
+++ b/src/chunk.rs
@@ -35,6 +35,12 @@ impl GenerationChunk {
Self { chunk_ids }
}
+ pub fn from_data_chunk(chunk: &DataChunk) -> anyhow::Result<Self> {
+ let data = chunk.data();
+ let data = std::str::from_utf8(data)?;
+ Ok(serde_json::from_str(data)?)
+ }
+
pub fn is_empty(&self) -> bool {
self.chunk_ids.is_empty()
}
diff --git a/src/chunkid.rs b/src/chunkid.rs
index 9eec41f..3933d4b 100644
--- a/src/chunkid.rs
+++ b/src/chunkid.rs
@@ -37,6 +37,10 @@ impl ChunkId {
}
}
+ pub fn from_str(s: &str) -> Self {
+ ChunkId { id: s.to_string() }
+ }
+
pub fn as_bytes(&self) -> &[u8] {
self.id.as_bytes()
}
@@ -81,7 +85,7 @@ impl FromStr for ChunkId {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
- Ok(ChunkId { id: s.to_string() })
+ Ok(ChunkId::from_str(s))
}
}
@@ -113,6 +117,6 @@ mod test {
fn survives_round_trip() {
let id = ChunkId::new();
let id_str = id.to_string();
- assert_eq!(id, id_str.parse().unwrap());
+ assert_eq!(id, ChunkId::from_str(&id_str))
}
}
diff --git a/src/client.rs b/src/client.rs
index 616ceef..1b507d3 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -4,6 +4,7 @@ use crate::chunk::GenerationChunk;
use crate::chunker::Chunker;
use crate::chunkid::ChunkId;
use crate::chunkmeta::ChunkMeta;
+use crate::error::ObnamError;
use crate::fsentry::{FilesystemEntry, FilesystemKind};
use crate::generation::{FinishedGeneration, LocalGeneration};
use crate::genlist::GenerationList;
@@ -212,24 +213,30 @@ impl BackupClient {
return Err(ClientError::ChunkNotFound(chunk_id.to_string()).into());
}
+ let headers = res.headers();
+ let meta = headers.get("chunk-meta");
+ if meta.is_none() {
+ return Err(ObnamError::NoChunkMeta(chunk_id.to_string()).into());
+ }
+ let meta = meta.unwrap().to_str()?;
+ let meta: ChunkMeta = serde_json::from_str(meta)?;
+
let body = res.bytes()?;
- Ok(DataChunk::new(body.to_vec()))
+ let body = body.to_vec();
+ let actual = sha256(&body);
+ if actual != meta.sha256() {
+ return Err(ObnamError::WrongChecksum(chunk_id.to_string()).into());
+ }
+
+ let chunk: DataChunk = DataChunk::new(body);
+
+ Ok(chunk)
}
fn fetch_generation_chunk(&self, gen_id: &str) -> anyhow::Result<GenerationChunk> {
- let url = format!("{}/{}", &self.chunks_url(), gen_id);
- trace!("fetch_generation_chunk: url={:?}", url);
- let req = self.client.get(&url).build()?;
- let res = self.client.execute(req)?;
- debug!("fetch_generation_chunk: status={}", res.status());
- if res.status() != 200 {
- return Err(ClientError::GenerationNotFound(gen_id.to_string()).into());
- }
-
- let text = res.text()?;
- debug!("fetch_generation_chunk: text={:?}", text);
- let gen: GenerationChunk = serde_json::from_str(&text)?;
- debug!("fetch_generation_chunk: {:?}", gen);
+ let chunk_id = ChunkId::from_str(gen_id);
+ let chunk = self.fetch_chunk(&chunk_id)?;
+ let gen = GenerationChunk::from_data_chunk(&chunk)?;
Ok(gen)
}
diff --git a/src/cmd/get_chunk.rs b/src/cmd/get_chunk.rs
new file mode 100644
index 0000000..bf653ff
--- /dev/null
+++ b/src/cmd/get_chunk.rs
@@ -0,0 +1,15 @@
+use crate::chunkid::ChunkId;
+use crate::client::BackupClient;
+use crate::client::ClientConfig;
+use std::io::{stdout, Write};
+
+pub fn get_chunk(config: &ClientConfig, chunk_id: &str) -> anyhow::Result<()> {
+ let client = BackupClient::new(&config.server_url)?;
+ let chunk_id: ChunkId = chunk_id.parse().unwrap();
+ let chunk = client.fetch_chunk(&chunk_id)?;
+
+ let stdout = stdout();
+ let mut handle = stdout.lock();
+ handle.write_all(chunk.data())?;
+ Ok(())
+}
diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs
index 2919d88..c3cebe2 100644
--- a/src/cmd/mod.rs
+++ b/src/cmd/mod.rs
@@ -9,3 +9,6 @@ pub use list_files::list_files;
pub mod restore;
pub use restore::restore;
+
+pub mod get_chunk;
+pub use get_chunk::get_chunk;
diff --git a/src/error.rs b/src/error.rs
index 3b3f573..360e62d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -9,4 +9,10 @@ pub enum ObnamError {
#[error("Generation has more than one file with the name {0}")]
TooManyFiles(PathBuf),
+
+ #[error("Server response did not have a 'chunk-meta' header for chunk {0}")]
+ NoChunkMeta(String),
+
+ #[error("Wrong checksum for chunk {0}")]
+ WrongChecksum(String),
}