diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-01-04 13:30:21 +0200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-01-04 15:02:33 +0200 |
commit | c11b8bb76c50cff6aa481f17907ae7200ac55c01 (patch) | |
tree | f275d30b15891a71f11d8012f0573268310567bf /src/cmd | |
parent | f73b2a919dc2fe2d92eef6df9b7cab25af083fb3 (diff) | |
download | obnam2-c11b8bb76c50cff6aa481f17907ae7200ac55c01.tar.gz |
feat! record whether file was backed up and why, in a generation
This changes SQL schema.
Diffstat (limited to 'src/cmd')
-rw-r--r-- | src/cmd/backup.rs | 74 | ||||
-rw-r--r-- | src/cmd/list_files.rs | 42 | ||||
-rw-r--r-- | src/cmd/mod.rs | 5 | ||||
-rw-r--r-- | src/cmd/restore.rs | 4 |
4 files changed, 105 insertions, 20 deletions
diff --git a/src/cmd/backup.rs b/src/cmd/backup.rs index 4d13fe7..1521cab 100644 --- a/src/cmd/backup.rs +++ b/src/cmd/backup.rs @@ -4,6 +4,9 @@ use crate::fsiter::FsIterator; use crate::generation::{LocalGeneration, NascentGeneration}; use indicatif::{ProgressBar, ProgressStyle}; use log::{debug, info}; +use rusqlite::types::ToSqlOutput; +use rusqlite::ToSql; +use std::fmt; use tempfile::NamedTempFile; pub fn backup(config: &ClientConfig, buffer_size: usize) -> anyhow::Result<()> { @@ -43,7 +46,9 @@ pub fn backup(config: &ClientConfig, buffer_size: usize) -> anyhow::Result<()> { let path = &entry.pathbuf(); info!("backup: {}", path.display()); progress.set_message(&format!("{}", path.display())); - client.upload_filesystem_entry(entry, buffer_size) + let (new_entry, ids) = + client.upload_filesystem_entry(entry, buffer_size)?; + Ok((new_entry, ids, Reason::IsNew)) } } }))?; @@ -60,16 +65,22 @@ pub fn backup(config: &ClientConfig, buffer_size: usize) -> anyhow::Result<()> { let path = &entry.pathbuf(); info!("backup: {}", path.display()); progress.set_message(&format!("{}", path.display())); - if needs_backup(&old, &entry) { - client.upload_filesystem_entry(entry, buffer_size) - } else { - let fileno = old.get_fileno(&entry.pathbuf())?; - let ids = if let Some(fileno) = fileno { - old.chunkids(fileno)? - } else { - vec![] - }; - Ok((entry.clone(), ids)) + let reason = needs_backup(&old, &entry); + match reason { + Reason::IsNew | Reason::Changed | Reason::Error => { + let (new_entry, ids) = + client.upload_filesystem_entry(entry, buffer_size)?; + Ok((new_entry, ids, reason)) + } + Reason::Unchanged => { + let fileno = old.get_fileno(&entry.pathbuf())?; + let ids = if let Some(fileno) = fileno { + old.chunkids(fileno)? + } else { + vec![] + }; + Ok((entry.clone(), ids, Reason::Unchanged)) + } } } } @@ -85,13 +96,42 @@ pub fn backup(config: &ClientConfig, buffer_size: usize) -> anyhow::Result<()> { let gen_id = client.upload_generation(&newname, buffer_size)?; println!("gen id: {}", gen_id); - // Delete the temporary file. + // Delete the temporary file.q std::fs::remove_file(&newname)?; std::fs::remove_file(&oldname)?; Ok(()) } +#[derive(Debug)] +pub enum Reason { + IsNew, + Changed, + Unchanged, + Error, +} + +impl ToSql for Reason { + fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { + Ok(ToSqlOutput::Owned(rusqlite::types::Value::Text(format!( + "{}", + self + )))) + } +} + +impl fmt::Display for Reason { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let reason = match self { + Reason::IsNew => "new", + Reason::Changed => "changed", + Reason::Unchanged => "unchanged", + Reason::Error => "error", + }; + write!(f, "{}", reason) + } +} + fn create_progress_bar(verbose: bool) -> ProgressBar { let progress = if verbose { ProgressBar::new(0) @@ -109,7 +149,7 @@ fn create_progress_bar(verbose: bool) -> ProgressBar { progress } -fn needs_backup(old: &LocalGeneration, new_entry: &FilesystemEntry) -> bool { +fn needs_backup(old: &LocalGeneration, new_entry: &FilesystemEntry) -> Reason { let new_name = new_entry.pathbuf(); match old.get_file(&new_name) { // File is not in old generation. @@ -118,17 +158,17 @@ fn needs_backup(old: &LocalGeneration, new_entry: &FilesystemEntry) -> bool { "needs_backup: file is not in old generation, needs backup: {:?}", new_name ); - true + Reason::IsNew } // File is in old generation. Has its metadata changed? Ok(Some(old_entry)) => { if file_has_changed(&old_entry, new_entry) { debug!("needs_backup: file has changed: {:?}", new_name); - true + Reason::Changed } else { debug!("needs_backup: file has NOT changed: {:?}", new_name); - false + Reason::Unchanged } } @@ -139,7 +179,7 @@ fn needs_backup(old: &LocalGeneration, new_entry: &FilesystemEntry) -> bool { "needs_backup: lookup in old generation returned error, ignored: {:?}: {}", new_name, err ); - true + Reason::Error } } } diff --git a/src/cmd/list_files.rs b/src/cmd/list_files.rs new file mode 100644 index 0000000..aa4bed0 --- /dev/null +++ b/src/cmd/list_files.rs @@ -0,0 +1,42 @@ +use crate::client::BackupClient; +use crate::client::ClientConfig; +use crate::error::ObnamError; +use crate::fsentry::{FilesystemEntry, FilesystemKind}; +use tempfile::NamedTempFile; + +pub fn list_files(config: &ClientConfig, gen_ref: &str) -> anyhow::Result<()> { + // Create a named temporary file. We don't meed the open file + // handle, so we discard that. + let dbname = { + let temp = NamedTempFile::new()?; + let (_, dbname) = temp.keep()?; + dbname + }; + + let client = BackupClient::new(&config.server_url)?; + + let genlist = client.list_generations()?; + let gen_id: String = match genlist.resolve(gen_ref) { + None => return Err(ObnamError::UnknownGeneration(gen_ref.to_string()).into()), + Some(id) => id, + }; + + let gen = client.fetch_generation(&gen_id, &dbname)?; + for (_, entry, reason) in gen.files()? { + println!("{}", format_entry(&entry, &reason)); + } + + // Delete the temporary file. + std::fs::remove_file(&dbname)?; + + Ok(()) +} + +fn format_entry(e: &FilesystemEntry, reason: &str) -> String { + let kind = match e.kind() { + FilesystemKind::Regular => "-", + FilesystemKind::Directory => "d", + FilesystemKind::Symlink => "l", + }; + format!("{} {} ({})", kind, e.pathbuf().display(), reason) +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index ca5ff42..2919d88 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,8 +1,11 @@ mod backup; -pub use backup::backup; +pub use backup::{backup, Reason}; mod list; pub use list::list; +mod list_files; +pub use list_files::list_files; + pub mod restore; pub use restore::restore; diff --git a/src/cmd/restore.rs b/src/cmd/restore.rs index 53e168a..0efdffb 100644 --- a/src/cmd/restore.rs +++ b/src/cmd/restore.rs @@ -35,10 +35,10 @@ pub fn restore(config: &ClientConfig, gen_ref: &str, to: &Path) -> anyhow::Resul let gen = client.fetch_generation(&gen_id, &dbname)?; info!("restore file count: {}", gen.file_count()?); let progress = create_progress_bar(gen.file_count()?, true); - for (fileid, entry) in gen.files()? { + for (fileid, entry, _) in gen.files()? { restore_generation(&client, &gen, fileid, &entry, &to, &progress)?; } - for (_, entry) in gen.files()? { + for (_, entry, _) in gen.files()? { if entry.is_dir() { restore_directory_metadata(&entry, &to)?; } |