summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml1
-rw-r--r--DONE.md4
-rw-r--r--NEWS.md4
-rw-r--r--README.md4
-rw-r--r--RELEASE.md4
-rw-r--r--code-of-conduct.md4
-rw-r--r--deny.toml4
-rw-r--r--src/backup_reason.rs3
-rw-r--r--src/backup_run.rs15
-rw-r--r--src/benchmark.rs38
-rw-r--r--src/chunker.rs6
-rw-r--r--src/client.rs111
-rw-r--r--src/cmd/backup.rs4
-rw-r--r--src/cmd/gen_info.rs4
-rw-r--r--src/cmd/get_chunk.rs4
-rw-r--r--src/cmd/list.rs4
-rw-r--r--src/cmd/list_files.rs4
-rw-r--r--src/cmd/resolve.rs4
-rw-r--r--src/cmd/restore.rs8
-rw-r--r--src/cmd/show_gen.rs4
-rw-r--r--src/lib.rs1
-rw-r--r--src/policy.rs9
-rw-r--r--tutorial.md4
23 files changed, 81 insertions, 167 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 676f306..17535db 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ description = "a backup program"
license = "AGPL-3.0-or-later"
homepage = "https://obnam.org/"
repository = "https://gitlab.com/larswirzenius/obnam"
+rust-version = "1.56.0"
[dependencies]
diff --git a/DONE.md b/DONE.md
index 5836c59..641c703 100644
--- a/DONE.md
+++ b/DONE.md
@@ -1,7 +1,3 @@
----
-title: Definition of done
-...
-
# Definition of done
This definition is not meant to be petty bureaucracy. It's meant to be
diff --git a/NEWS.md b/NEWS.md
index 61ca4b7..f595634 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,4 @@
----
-title: Release notes for Obnam2
-...
+# Release notes for Obnam2
This file summarizes changes between releases of the second generation
of Obnam, the backup software. The software is technically called
diff --git a/README.md b/README.md
index 70691d5..a9583cd 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,3 @@
----
-title: Obnam — a backup system
-...
-
# Obnam — a backup system
Obnam2 is a project to develop a backup system.
diff --git a/RELEASE.md b/RELEASE.md
index bf30633..f264d4f 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -1,7 +1,3 @@
----
-title: Release checklist for Obnam
-...
-
# Release checklist for Obnam
Follow these steps to make a release of Obnam.
diff --git a/code-of-conduct.md b/code-of-conduct.md
index 8851723..ebc4a55 100644
--- a/code-of-conduct.md
+++ b/code-of-conduct.md
@@ -1,7 +1,3 @@
----
-title: Contributor Covenant Code of Conduct
-...
-
# Contributor Covenant Code of Conduct
## Our Pledge
diff --git a/deny.toml b/deny.toml
index 9db4685..08ad9ef 100644
--- a/deny.toml
+++ b/deny.toml
@@ -9,8 +9,8 @@ db-path = "~/.cargo/advisory-db"
db-urls = ["https://github.com/rustsec/advisory-db"]
vulnerability = "deny"
unmaintained = "warn"
-yanked = "allow"
-notice = "warn"
+yanked = "deny"
+notice = "deny"
ignore = [
"RUSTSEC-2020-0027",
"RUSTSEC-2020-0071",
diff --git a/src/backup_reason.rs b/src/backup_reason.rs
index 590f470..9a17d80 100644
--- a/src/backup_reason.rs
+++ b/src/backup_reason.rs
@@ -7,7 +7,8 @@ use std::fmt;
/// Represent the reason a file is in a backup.
#[derive(Debug, Copy, Clone)]
pub enum Reason {
- /// File was skipped for some reason, but carried over without changes.
+ /// File was skipped due to policy, but carried over without
+ /// changes.
Skipped,
/// File is new, compared to previous backup.
IsNew,
diff --git a/src/backup_run.rs b/src/backup_run.rs
index ade5ee0..3787494 100644
--- a/src/backup_run.rs
+++ b/src/backup_run.rs
@@ -3,9 +3,9 @@
use crate::backup_progress::BackupProgress;
use crate::backup_reason::Reason;
use crate::chunk::{GenerationChunk, GenerationChunkError};
-use crate::chunker::{Chunker, ChunkerError};
+use crate::chunker::{ChunkerError, FileChunks};
use crate::chunkid::ChunkId;
-use crate::client::{AsyncBackupClient, ClientError};
+use crate::client::{BackupClient, ClientError};
use crate::config::ClientConfig;
use crate::error::ObnamError;
use crate::fsentry::{FilesystemEntry, FilesystemKind};
@@ -24,7 +24,7 @@ const SQLITE_CHUNK_SIZE: usize = MIB as usize;
/// A running backup.
pub struct BackupRun<'a> {
- client: &'a AsyncBackupClient,
+ client: &'a BackupClient,
policy: BackupPolicy,
buffer_size: usize,
progress: Option<BackupProgress>,
@@ -95,10 +95,7 @@ pub struct RootsBackupOutcome {
impl<'a> BackupRun<'a> {
/// Create a new run for an initial backup.
- pub fn initial(
- config: &ClientConfig,
- client: &'a AsyncBackupClient,
- ) -> Result<Self, BackupError> {
+ pub fn initial(config: &ClientConfig, client: &'a BackupClient) -> Result<Self, BackupError> {
Ok(Self {
client,
policy: BackupPolicy::default(),
@@ -110,7 +107,7 @@ impl<'a> BackupRun<'a> {
/// Create a new run for an incremental backup.
pub fn incremental(
config: &ClientConfig,
- client: &'a AsyncBackupClient,
+ client: &'a BackupClient,
) -> Result<Self, BackupError> {
Ok(Self {
client,
@@ -361,7 +358,7 @@ impl<'a> BackupRun<'a> {
let mut chunk_ids = vec![];
let file = std::fs::File::open(filename)
.map_err(|err| ClientError::FileOpen(filename.to_path_buf(), err))?;
- let chunker = Chunker::new(size, file, filename);
+ let chunker = FileChunks::new(size, file, filename);
for item in chunker {
let chunk = item?;
if let Some(chunk_id) = self.client.has_chunk(chunk.meta()).await? {
diff --git a/src/benchmark.rs b/src/benchmark.rs
deleted file mode 100644
index d2d9003..0000000
--- a/src/benchmark.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-//! Benchmark chunk generation.
-//!
-//! This is only for development.
-
-use crate::checksummer::Checksum;
-use crate::chunk::DataChunk;
-use crate::chunkid::ChunkId;
-use crate::chunkmeta::ChunkMeta;
-
-/// Generate a desired number of empty data chunks with id and metadata.
-pub struct ChunkGenerator {
- goal: u32,
- next: u32,
-}
-
-impl ChunkGenerator {
- /// Create a new ChunkGenerator.
- pub fn new(goal: u32) -> Self {
- Self { goal, next: 0 }
- }
-}
-
-impl Iterator for ChunkGenerator {
- type Item = (ChunkId, Checksum, DataChunk);
-
- fn next(&mut self) -> Option<Self::Item> {
- if self.next >= self.goal {
- None
- } else {
- let id = ChunkId::recreate(&format!("{}", self.next));
- let checksum = id.sha256();
- let meta = ChunkMeta::new(&checksum);
- let chunk = DataChunk::new(vec![], meta);
- self.next += 1;
- Some((id, checksum, chunk))
- }
- }
-}
diff --git a/src/chunker.rs b/src/chunker.rs
index e8e31e1..7954621 100644
--- a/src/chunker.rs
+++ b/src/chunker.rs
@@ -7,7 +7,7 @@ use std::io::prelude::*;
use std::path::{Path, PathBuf};
/// Iterator over chunks in a file.
-pub struct Chunker {
+pub struct FileChunks {
chunk_size: usize,
buf: Vec<u8>,
filename: PathBuf,
@@ -22,7 +22,7 @@ pub enum ChunkerError {
FileRead(PathBuf, std::io::Error),
}
-impl Chunker {
+impl FileChunks {
/// Create new iterator.
pub fn new(chunk_size: usize, handle: std::fs::File, filename: &Path) -> Self {
let mut buf = vec![];
@@ -61,7 +61,7 @@ impl Chunker {
}
}
-impl Iterator for Chunker {
+impl Iterator for FileChunks {
type Item = Result<DataChunk, ChunkerError>;
/// Return the next chunk, if any, or an error.
diff --git a/src/client.rs b/src/client.rs
index ed6b86b..bcc31b4 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -96,81 +96,17 @@ pub enum ClientError {
}
/// Client for the Obnam server HTTP API.
-///
-/// This is the async version.
-pub struct AsyncBackupClient {
- chunk_client: AsyncChunkClient,
-}
-
-impl AsyncBackupClient {
- /// Create a new backup client.
- pub fn new(config: &ClientConfig) -> Result<Self, ClientError> {
- info!("creating backup client with config: {:#?}", config);
- Ok(Self {
- chunk_client: AsyncChunkClient::new(config)?,
- })
- }
-
- /// Does the server have a chunk?
- pub async fn has_chunk(&self, meta: &ChunkMeta) -> Result<Option<ChunkId>, ClientError> {
- self.chunk_client.has_chunk(meta).await
- }
-
- /// Upload a data chunk to the srver.
- pub async fn upload_chunk(&self, chunk: DataChunk) -> Result<ChunkId, ClientError> {
- self.chunk_client.upload_chunk(chunk).await
- }
-
- /// List backup generations known by the server.
- pub async fn list_generations(&self) -> Result<GenerationList, ClientError> {
- self.chunk_client.list_generations().await
- }
-
- /// Fetch a data chunk from the server, given the chunk identifier.
- pub async fn fetch_chunk(&self, chunk_id: &ChunkId) -> Result<DataChunk, ClientError> {
- self.chunk_client.fetch_chunk(chunk_id).await
- }
-
- async fn fetch_generation_chunk(&self, gen_id: &GenId) -> Result<GenerationChunk, ClientError> {
- let chunk = self.fetch_chunk(gen_id.as_chunk_id()).await?;
- let gen = GenerationChunk::from_data_chunk(&chunk)?;
- Ok(gen)
- }
-
- /// Fetch a backup generation's metadata, given it's identifier.
- pub async fn fetch_generation(
- &self,
- gen_id: &GenId,
- dbname: &Path,
- ) -> Result<LocalGeneration, ClientError> {
- let gen = self.fetch_generation_chunk(gen_id).await?;
-
- // Fetch the SQLite file, storing it in the named file.
- let mut dbfile = File::create(&dbname)
- .map_err(|err| ClientError::FileCreate(dbname.to_path_buf(), err))?;
- for id in gen.chunk_ids() {
- let chunk = self.fetch_chunk(id).await?;
- dbfile
- .write_all(chunk.data())
- .map_err(|err| ClientError::FileWrite(dbname.to_path_buf(), err))?;
- }
- info!("downloaded generation to {}", dbname.display());
-
- let gen = LocalGeneration::open(dbname)?;
- Ok(gen)
- }
-}
-
-/// Client for using chunk part of Obnam server HTTP API.
-pub struct AsyncChunkClient {
+pub struct BackupClient {
client: reqwest::Client,
base_url: String,
cipher: CipherEngine,
}
-impl AsyncChunkClient {
- /// Create a new chunk client.
+impl BackupClient {
+ /// Create a new backup client.
pub fn new(config: &ClientConfig) -> Result<Self, ClientError> {
+ info!("creating backup client with config: {:#?}", config);
+
let pass = config.passwords()?;
let client = reqwest::Client::builder()
@@ -192,7 +128,7 @@ impl AsyncChunkClient {
format!("{}/chunks", self.base_url())
}
- /// Does server have a chunk?
+ /// Does the server have a chunk?
pub async fn has_chunk(&self, meta: &ChunkMeta) -> Result<Option<ChunkId>, ClientError> {
let body = match self.get("", &[("sha256", meta.sha256())]).await {
Ok((_, body)) => body,
@@ -211,7 +147,7 @@ impl AsyncChunkClient {
Ok(has)
}
- /// Upload a new chunk to the server.
+ /// Upload a data chunk to the srver.
pub async fn upload_chunk(&self, chunk: DataChunk) -> Result<ChunkId, ClientError> {
let enc = self.cipher.encrypt_chunk(&chunk)?;
let res = self
@@ -234,7 +170,7 @@ impl AsyncChunkClient {
Ok(chunk_id)
}
- /// List all generation chunks on the server.
+ /// List backup generations known by the server.
pub async fn list_generations(&self) -> Result<GenerationList, ClientError> {
let (_, body) = self.get("", &[("generation", "true")]).await?;
@@ -248,7 +184,7 @@ impl AsyncChunkClient {
Ok(GenerationList::new(finished))
}
- /// Fetch a chunk from the server, given its id.
+ /// Fetch a data chunk from the server, given the chunk identifier.
pub async fn fetch_chunk(&self, chunk_id: &ChunkId) -> Result<DataChunk, ClientError> {
let (headers, body) = self.get(&format!("/{}", chunk_id), &[]).await?;
let meta = self.get_chunk_meta_header(chunk_id, &headers)?;
@@ -259,6 +195,35 @@ impl AsyncChunkClient {
Ok(chunk)
}
+ async fn fetch_generation_chunk(&self, gen_id: &GenId) -> Result<GenerationChunk, ClientError> {
+ let chunk = self.fetch_chunk(gen_id.as_chunk_id()).await?;
+ let gen = GenerationChunk::from_data_chunk(&chunk)?;
+ Ok(gen)
+ }
+
+ /// Fetch a backup generation's metadata, given it's identifier.
+ pub async fn fetch_generation(
+ &self,
+ gen_id: &GenId,
+ dbname: &Path,
+ ) -> Result<LocalGeneration, ClientError> {
+ let gen = self.fetch_generation_chunk(gen_id).await?;
+
+ // Fetch the SQLite file, storing it in the named file.
+ let mut dbfile = File::create(&dbname)
+ .map_err(|err| ClientError::FileCreate(dbname.to_path_buf(), err))?;
+ for id in gen.chunk_ids() {
+ let chunk = self.fetch_chunk(id).await?;
+ dbfile
+ .write_all(chunk.data())
+ .map_err(|err| ClientError::FileWrite(dbname.to_path_buf(), err))?;
+ }
+ info!("downloaded generation to {}", dbname.display());
+
+ let gen = LocalGeneration::open(dbname)?;
+ Ok(gen)
+ }
+
async fn get(
&self,
path: &str,
diff --git a/src/cmd/backup.rs b/src/cmd/backup.rs
index 6e09d37..92b0f40 100644
--- a/src/cmd/backup.rs
+++ b/src/cmd/backup.rs
@@ -1,7 +1,7 @@
//! The `backup` subcommand.
use crate::backup_run::BackupRun;
-use crate::client::AsyncBackupClient;
+use crate::client::BackupClient;
use crate::config::ClientConfig;
use crate::error::ObnamError;
use crate::generation::GenId;
@@ -26,7 +26,7 @@ impl Backup {
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
let runtime = SystemTime::now();
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let genlist = client.list_generations().await?;
let oldtemp = NamedTempFile::new()?;
diff --git a/src/cmd/gen_info.rs b/src/cmd/gen_info.rs
index 2663d9b..2ce1f64 100644
--- a/src/cmd/gen_info.rs
+++ b/src/cmd/gen_info.rs
@@ -1,6 +1,6 @@
//! The `gen-info` subcommand.
-use crate::client::AsyncBackupClient;
+use crate::client::BackupClient;
use crate::config::ClientConfig;
use crate::error::ObnamError;
use log::info;
@@ -26,7 +26,7 @@ impl GenInfo {
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
let temp = NamedTempFile::new()?;
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let genlist = client.list_generations().await?;
let gen_id = genlist.resolve(&self.gen_ref)?;
diff --git a/src/cmd/get_chunk.rs b/src/cmd/get_chunk.rs
index 905e997..0b27084 100644
--- a/src/cmd/get_chunk.rs
+++ b/src/cmd/get_chunk.rs
@@ -1,7 +1,7 @@
//! The `get-chunk` subcommand.
use crate::chunkid::ChunkId;
-use crate::client::AsyncBackupClient;
+use crate::client::BackupClient;
use crate::config::ClientConfig;
use crate::error::ObnamError;
use std::io::{stdout, Write};
@@ -24,7 +24,7 @@ impl GetChunk {
}
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let chunk_id: ChunkId = self.chunk_id.parse().unwrap();
let chunk = client.fetch_chunk(&chunk_id).await?;
let stdout = stdout();
diff --git a/src/cmd/list.rs b/src/cmd/list.rs
index 6c58e30..f176594 100644
--- a/src/cmd/list.rs
+++ b/src/cmd/list.rs
@@ -1,6 +1,6 @@
//! The `list` subcommand.
-use crate::client::AsyncBackupClient;
+use crate::client::BackupClient;
use crate::config::ClientConfig;
use crate::error::ObnamError;
use structopt::StructOpt;
@@ -18,7 +18,7 @@ impl List {
}
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let generations = client.list_generations().await?;
for finished in generations.iter() {
diff --git a/src/cmd/list_files.rs b/src/cmd/list_files.rs
index 888943e..12d34b1 100644
--- a/src/cmd/list_files.rs
+++ b/src/cmd/list_files.rs
@@ -1,7 +1,7 @@
//! The `list-files` subcommand.
use crate::backup_reason::Reason;
-use crate::client::AsyncBackupClient;
+use crate::client::BackupClient;
use crate::config::ClientConfig;
use crate::error::ObnamError;
use crate::fsentry::{FilesystemEntry, FilesystemKind};
@@ -27,7 +27,7 @@ impl ListFiles {
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
let temp = NamedTempFile::new()?;
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let genlist = client.list_generations().await?;
let gen_id = genlist.resolve(&self.gen_id)?;
diff --git a/src/cmd/resolve.rs b/src/cmd/resolve.rs
index cd08908..3b9570a 100644
--- a/src/cmd/resolve.rs
+++ b/src/cmd/resolve.rs
@@ -1,6 +1,6 @@
//! The `resolve` subcommand.
-use crate::client::AsyncBackupClient;
+use crate::client::BackupClient;
use crate::config::ClientConfig;
use crate::error::ObnamError;
use structopt::StructOpt;
@@ -21,7 +21,7 @@ impl Resolve {
}
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let generations = client.list_generations().await?;
match generations.resolve(&self.generation) {
diff --git a/src/cmd/restore.rs b/src/cmd/restore.rs
index 2a36986..7b3d95e 100644
--- a/src/cmd/restore.rs
+++ b/src/cmd/restore.rs
@@ -1,7 +1,7 @@
//! The `restore` subcommand.
use crate::backup_reason::Reason;
-use crate::client::{AsyncBackupClient, ClientError};
+use crate::client::{BackupClient, ClientError};
use crate::config::ClientConfig;
use crate::error::ObnamError;
use crate::fsentry::{FilesystemEntry, FilesystemKind};
@@ -43,7 +43,7 @@ impl Restore {
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
let temp = NamedTempFile::new()?;
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let genlist = client.list_generations().await?;
let gen_id = genlist.resolve(&self.gen_id)?;
@@ -130,7 +130,7 @@ pub enum RestoreError {
}
async fn restore_generation(
- client: &AsyncBackupClient,
+ client: &BackupClient,
gen: &LocalGeneration,
fileid: i64,
entry: &FilesystemEntry,
@@ -182,7 +182,7 @@ fn restored_path(entry: &FilesystemEntry, to: &Path) -> Result<PathBuf, RestoreE
}
async fn restore_regular(
- client: &AsyncBackupClient,
+ client: &BackupClient,
gen: &LocalGeneration,
path: &Path,
fileid: i64,
diff --git a/src/cmd/show_gen.rs b/src/cmd/show_gen.rs
index 6ec1203..6c8ba19 100644
--- a/src/cmd/show_gen.rs
+++ b/src/cmd/show_gen.rs
@@ -1,6 +1,6 @@
//! The `show-generation` subcommand.
-use crate::client::AsyncBackupClient;
+use crate::client::BackupClient;
use crate::config::ClientConfig;
use crate::error::ObnamError;
use crate::fsentry::FilesystemKind;
@@ -26,7 +26,7 @@ impl ShowGeneration {
async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
let temp = NamedTempFile::new()?;
- let client = AsyncBackupClient::new(config)?;
+ let client = BackupClient::new(config)?;
let genlist = client.list_generations().await?;
let gen_id = genlist.resolve(&self.gen_id)?;
diff --git a/src/lib.rs b/src/lib.rs
index 957ec13..8961df4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,7 +8,6 @@
pub mod backup_progress;
pub mod backup_reason;
pub mod backup_run;
-pub mod benchmark;
pub mod checksummer;
pub mod chunk;
pub mod chunker;
diff --git a/src/policy.rs b/src/policy.rs
index 9b66c1d..7241f0f 100644
--- a/src/policy.rs
+++ b/src/policy.rs
@@ -6,6 +6,15 @@ use crate::generation::LocalGeneration;
use log::{debug, warn};
/// Policy for what gets backed up.
+///
+/// The policy allows two aspects to be controlled:
+///
+/// * should new files )(files that didn't exist in the previous
+/// backup be included in the new backup?
+/// * should files that haven't been changed since the previous backup
+/// be included in the new backup?
+///
+/// If policy doesn't allow a file to be included, it's skipped.
pub struct BackupPolicy {
new: bool,
old_if_changed: bool,
diff --git a/tutorial.md b/tutorial.md
index 3435a64..b20c84e 100644
--- a/tutorial.md
+++ b/tutorial.md
@@ -1,6 +1,4 @@
----
-title: Obnam tutorial
-...
+# Obnam tutorial
With the help of this tutorial, you're going to set up Obnam, make your first
backup, and check that you can restore files from it.