From c4b81fcfc154a88972f46ca7947badd7d4d3708b Mon Sep 17 00:00:00 2001 From: Marius Nuennerich Date: Sat, 30 Mar 2024 22:54:47 +0100 Subject: [PATCH] Add support for relative paths --- Cargo.lock | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 31 ++++++++++++++++++++++++++----- src/main.rs | 32 +++++++++++++++++++++++++------- 4 files changed, 105 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 92c8fbd..8e4ad9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,7 @@ name = "bacify" version = "0.1.0" dependencies = [ "chrono", + "clap", "dirs", "env_logger", "generic-array 1.0.0", @@ -148,6 +149,46 @@ dependencies = [ "windows-targets 0.52.4", ] +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + [[package]] name = "colorchoice" version = "1.0.0" @@ -279,6 +320,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "humantime" version = "2.1.0" @@ -516,6 +563,12 @@ dependencies = [ "digest", ] +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "syn" version = "2.0.55" diff --git a/Cargo.toml b/Cargo.toml index e5c3582..a80ea0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] chrono = "0.4" +clap = { version = "4.5.4", features = ["derive"] } dirs = "5.0.1" env_logger = "0.11.3" generic-array = "1.0.0" diff --git a/README.md b/README.md index 45b04a2..88fa00b 100644 --- a/README.md +++ b/README.md @@ -17,26 +17,47 @@ Only the fantastic [restic](https://github.com/restic/restic) is supported at th Set the `RESTIC_REPOSITORY` and `RESTIC_PASSWORD` environment variables and run `cargo run`. -### Example (assuming you cloned Bacify into *$HOME/dev/bacify*): +### Examples + +NOTE: Assuming you cloned Bacify into *$HOME/dev/bacify* + +#### Backup snapshot with an absolute path Create backup and verify the data in the repository: ``` +$ cd $HOME/dev/bacify $ export RESTIC_REPOSITORY="$HOME/tmp/restic-repo" $ export RESTIC_PASSWORD="foo" $ restic init $ restic backup $HOME/dev/bacify -$ restic check --read-data ``` Verify the backup against the local files: ``` -$ cd ~/dev/bacify +$ cargo run +``` + +#### Backup snapshot with a relative path + +Create backup and verify the data in the repository: +``` +$ cd $HOME/dev/bacify $ export RESTIC_REPOSITORY="$HOME/tmp/restic-repo" $ export RESTIC_PASSWORD="foo" -$ export LOG_LEVEL=debug -$ cargo run +$ restic init +$ restic backup . +``` + +Verify the backup against the local files: +``` +$ cargo run -- --relative-path ``` +*--relative-path* is needed as the snapshot metadata lists absolute paths, +but the files are actually restored without the leading path components. + +### Excludes + > [!WARNING] > Read this is you get a lot of errors about missing files!
> At the moment there is only support for a hard-coded, single exclude file named `$HOME/.backup_exclude`.
diff --git a/src/main.rs b/src/main.rs index fb9cc47..1b7d5b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use chrono::prelude::*; +use clap::Parser; use env_logger::{Builder, Env, Target}; use log::{debug, info, warn}; use serde_json::Value; @@ -19,10 +20,17 @@ struct BackupVerifier { source_dir: PathBuf, id: String, excludes: Vec, + relative_path: bool, +} + +#[derive(Parser, Debug)] +struct Args { + #[arg(short, long)] + relative_path: bool, } impl BackupVerifier { - fn new() -> BackupVerifier { + fn new(relative_path: bool) -> BackupVerifier { BackupVerifier { missing: HashSet::new(), corrupt: HashSet::new(), @@ -31,6 +39,7 @@ impl BackupVerifier { source_dir: PathBuf::new(), id: String::new(), excludes: Vec::new(), + relative_path, } } @@ -53,11 +62,18 @@ impl BackupVerifier { // Verify the source file against the backup fn verify_source_file(&mut self, file: &Path) -> io::Result<()> { - // If file is an absolute Path we need to strip the leading slash, otherwise - // backup_dir.join(file) will return file, instead of the joined paths. - // See https://doc.rust-lang.org/std/path/struct.Path.html#method.join. - // TODO: Add support for relative paths. - let relative_file = file.strip_prefix("/").unwrap_or(file); + // Relative paths restore right into the temporary directory, but in the snapshot metadata + // there is an absolute path. + // Use --relative-path (or -r) to remove the leading path components. + let relative_file = if self.relative_path { + file.strip_prefix(self.source_dir.as_path()) + .expect("Could not strip prefix") + } else { + // If file is an absolute Path we need to strip the leading slash, otherwise + // backup_dir.join(file) will return file, instead of the joined paths. + // See https://doc.rust-lang.org/std/path/struct.Path.html#method.join. + file.strip_prefix("/").unwrap_or(file) + }; let counterpart = self.backup_dir.join(relative_file); let file_metadata = fs::metadata(file)?; @@ -216,12 +232,14 @@ fn main() { .target(Target::Stdout) .init(); + let args = Args::parse(); + // We want to see some output during restore, needs at least restic version 0.16.0 if std::env::var_os("RESTIC_PROGRESS_FPS").is_none() { std::env::set_var("RESTIC_PROGRESS_FPS", "0.5"); } - let mut verifier = BackupVerifier::new(); + let mut verifier = BackupVerifier::new(args.relative_path); match verifier.main() { Err(e) => { info!("Error: {}", e);