From 2db3f56f91c161f64cff110946522c50cd73efcb Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Fri, 22 Jul 2022 12:59:50 +0200 Subject: [PATCH 1/5] new command: config --- src/commands/config.rs | 76 ++++++++++++++++++++++++++++++++++++++++++ src/commands/mod.rs | 5 +++ src/repo/configfile.rs | 5 ++- 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/commands/config.rs diff --git a/src/commands/config.rs b/src/commands/config.rs new file mode 100644 index 0000000..666b3e2 --- /dev/null +++ b/src/commands/config.rs @@ -0,0 +1,76 @@ +use anyhow::{bail, Result}; +use clap::Parser; + +use crate::backend::DecryptFullBackend; +use crate::repo::ConfigFile; + +#[derive(Parser)] +pub(super) struct Opts { + #[clap(flatten)] + config_opts: ConfigOpts, +} + +pub(super) async fn execute( + be: &impl DecryptFullBackend, + opts: Opts, + config: ConfigFile, +) -> Result<()> { + let mut new_config = config.clone(); + opts.config_opts.apply(&mut new_config)?; + if new_config != config { + be.save_file(&new_config).await?; + println!("saved new config"); + } else { + println!("config is unchanged"); + } + + Ok(()) +} + +#[derive(Parser)] +pub(super) struct ConfigOpts { + /// set compression level, 0 equals no compression + #[clap(long, value_name = "LEVEL")] + set_compression: Option, + + /// set repository version + #[clap(long, value_name = "VERSION")] + set_version: Option, +} + +impl ConfigOpts { + pub fn apply(&self, config: &mut ConfigFile) -> Result<()> { + if let Some(version) = self.set_version { + let range = 1..=2; + if !range.contains(&version) { + bail!( + "version {version} is not supported. Allowed values: {}..{}", + range.start(), + range.end() + ); + } else if version < config.version { + bail!( + "cannot downgrade version from {} to {version}", + config.version + ); + } + config.version = version; + } + + if let Some(compression) = self.set_compression { + if config.version == 1 && compression != 0 { + bail!("compression level {compression} is not supported for repo v1"); + } + let range = zstd::compression_level_range(); + if !range.contains(&compression) { + bail!( + "compression level {compression} is not supported. Allowed values: 0..{}", + range.end() + ); + } + config.compression = Some(compression); + } + + Ok(()) + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 24e8603..40321d8 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -12,6 +12,7 @@ use crate::repo::ConfigFile; mod backup; mod cat; mod check; +mod config; mod diff; mod forget; mod helpers; @@ -76,6 +77,9 @@ enum Command { /// Cat repository files and blobs Cat(cat::Opts), + /// Change repo configuration + Config(config::Opts), + /// Check repository Check(check::Opts), @@ -171,6 +175,7 @@ pub async fn execute() -> Result<()> { match cmd { Command::Backup(opts) => backup::execute(&dbe, opts, config, command).await?, + Command::Config(opts) => config::execute(&dbe, opts, config).await?, Command::Cat(opts) => cat::execute(&dbe, opts).await?, Command::Check(opts) => check::execute(&dbe, &cache, &be_hot, &be, opts).await?, Command::Diff(opts) => diff::execute(&dbe, opts).await?, diff --git a/src/repo/configfile.rs b/src/repo/configfile.rs index 480a82d..3f5719b 100644 --- a/src/repo/configfile.rs +++ b/src/repo/configfile.rs @@ -4,13 +4,15 @@ use serde::{Deserialize, Serialize}; use crate::backend::{FileType, RepoFile}; use crate::id::Id; -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] pub struct ConfigFile { pub version: u32, pub id: Id, pub chunker_polynomial: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub is_hot: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub compression: Option, } impl RepoFile for ConfigFile { @@ -24,6 +26,7 @@ impl ConfigFile { id, chunker_polynomial: format!("{:x}", poly), is_hot: None, + compression: None, } } From 0d102c09e10bd60bb918b002575608e8341eda6c Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Fri, 22 Jul 2022 17:16:23 +0200 Subject: [PATCH 2/5] local BE: overwrite existing files --- src/backend/local.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/local.rs b/src/backend/local.rs index f0ea307..c4e9bc5 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -139,7 +139,7 @@ impl WriteBackend for LocalBackend { v3!("writing tpe: {:?}, id: {}", &tpe, &id); let filename = self.path(tpe, id); let mut file = fs::OpenOptions::new() - .create_new(true) + .create(true) .write(true) .open(&filename)?; copy(&mut f, &mut file)?; @@ -151,7 +151,7 @@ impl WriteBackend for LocalBackend { v3!("writing tpe: {:?}, id: {}", &tpe, &id); let filename = self.path(tpe, id); let mut file = fs::OpenOptions::new() - .create_new(true) + .create(true) .write(true) .open(&filename)?; file.write_all(&buf)?; From f4544f1dab5022f5a7777b883a350e7569cc3f38 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Fri, 22 Jul 2022 17:16:54 +0200 Subject: [PATCH 3/5] backup/prune: use compression from config file --- src/commands/backup.rs | 8 ++------ src/commands/prune.rs | 6 +----- src/repo/configfile.rs | 13 +++++++++++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/commands/backup.rs b/src/commands/backup.rs index b8885ea..b602479 100644 --- a/src/commands/backup.rs +++ b/src/commands/backup.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, Result}; use chrono::{Duration, Local}; use clap::Parser; use gethostname::gethostname; @@ -57,11 +57,7 @@ pub(super) async fn execute( ) -> Result<()> { let time = Local::now(); let poly = config.poly()?; - let zstd = match config.version { - 1 => None, - 2 => Some(0), - _ => bail!("config version not supported!"), - }; + let zstd = config.zstd()?; let mut be = DryRunBackend::new(be.clone(), opts.dry_run); be.set_zstd(zstd); diff --git a/src/commands/prune.rs b/src/commands/prune.rs index dbb3575..ed32fc6 100644 --- a/src/commands/prune.rs +++ b/src/commands/prune.rs @@ -715,11 +715,7 @@ impl Pruner { opts: Opts, config: ConfigFile, ) -> Result<()> { - let zstd = match config.version { - 1 => None, - 2 => Some(0), - _ => bail!("config version not supported!"), - }; + let zstd = config.zstd()?; let mut be = be.clone(); be.set_zstd(zstd); diff --git a/src/repo/configfile.rs b/src/repo/configfile.rs index 3f5719b..9e9d913 100644 --- a/src/repo/configfile.rs +++ b/src/repo/configfile.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; use crate::backend::{FileType, RepoFile}; @@ -12,7 +12,7 @@ pub struct ConfigFile { #[serde(default, skip_serializing_if = "Option::is_none")] pub is_hot: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub compression: Option, + pub compression: Option, // note that Some(0) means no compression. } impl RepoFile for ConfigFile { @@ -33,4 +33,13 @@ impl ConfigFile { pub fn poly(&self) -> Result { Ok(u64::from_str_radix(&self.chunker_polynomial, 16)?) } + + pub fn zstd(&self) -> Result> { + match (self.version, self.compression) { + (1, _) | (2, Some(0)) => Ok(None), + (2, None) => Ok(Some(0)), // use default (=0) zstd compression + (2, Some(c)) => Ok(Some(c)), + _ => bail!("config version not supported!"), + } + } } From 5017d258f48c945a02eaffa2bbc4cbcbf3fb0324 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Fri, 22 Jul 2022 17:19:46 +0200 Subject: [PATCH 4/5] init: Add config options --- src/commands/config.rs | 4 ++-- src/commands/init.rs | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/commands/config.rs b/src/commands/config.rs index 666b3e2..8e63758 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -31,11 +31,11 @@ pub(super) async fn execute( pub(super) struct ConfigOpts { /// set compression level, 0 equals no compression #[clap(long, value_name = "LEVEL")] - set_compression: Option, + pub set_compression: Option, /// set repository version #[clap(long, value_name = "VERSION")] - set_version: Option, + pub set_version: Option, } impl ConfigOpts { diff --git a/src/commands/init.rs b/src/commands/init.rs index 984cfec..bae7421 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -5,6 +5,7 @@ use anyhow::{bail, Result}; use clap::Parser; use rpassword::{prompt_password, read_password_from_bufread}; +use super::config::ConfigOpts; use super::key::AddOpts; use crate::backend::{DecryptBackend, DecryptWriteBackend, FileType, WriteBackend}; use crate::chunker; @@ -16,6 +17,9 @@ use crate::repo::{ConfigFile, KeyFile}; pub(super) struct Opts { #[clap(flatten)] key_opts: AddOpts, + + #[clap(flatten)] + config_opts: ConfigOpts, } pub(super) async fn execute( @@ -28,6 +32,17 @@ pub(super) async fn execute( bail!("Config file already exists. Aborting."); } + // Create config first to allow catching errors from here without writing anything + let repo_id = Id::random(); + let chunker_poly = chunker::random_poly()?; + let version = match opts.config_opts.set_version { + None => 2, + Some(_) => 1, // will be changed later + }; + let mut config = ConfigFile::new(version, repo_id, chunker_poly); + opts.config_opts.apply(&mut config)?; + + // generate key let key = Key::new(); let key_opts = opts.key_opts; @@ -56,10 +71,7 @@ pub(super) async fn execute( } println!("key {} successfully added.", id); - let repo_id = Id::random(); - let chunker_poly = chunker::random_poly()?; - let mut config = ConfigFile::new(2, repo_id, chunker_poly); - + // save config let dbe = DecryptBackend::new(be, key.clone()); dbe.save_file(&config).await?; From c723ebffe75c155cd2bfc5b18033f0a1b7c12cac Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Fri, 22 Jul 2022 22:28:42 +0200 Subject: [PATCH 5/5] cat: don't require an id --- src/commands/cat.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/cat.rs b/src/commands/cat.rs index e085ad3..a151bfd 100644 --- a/src/commands/cat.rs +++ b/src/commands/cat.rs @@ -21,14 +21,14 @@ pub(super) struct Opts { enum Command { TreeBlob(IdOpt), DataBlob(IdOpt), - Config(IdOpt), + Config, Index(IdOpt), Snapshot(IdOpt), /// display a tree within a snapshot Tree(TreeOpts), } -#[derive(Parser)] +#[derive(Default, Parser)] struct IdOpt { /// id to cat id: String, @@ -45,7 +45,7 @@ struct TreeOpts { pub(super) async fn execute(be: &impl DecryptReadBackend, opts: Opts) -> Result<()> { match opts.command { - Command::Config(opt) => cat_file(be, FileType::Config, opt).await, + Command::Config => cat_file(be, FileType::Config, IdOpt::default()).await, Command::Index(opt) => cat_file(be, FileType::Index, opt).await, Command::Snapshot(opt) => cat_file(be, FileType::Snapshot, opt).await, // special treatment for catingg blobs: read the index and use it to locate the blob