From cbf2a8ad25536c17372f422959272ba86514a1ea Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 27 Aug 2022 07:52:30 +0200 Subject: [PATCH] backup: Allow to use stdin as source --- src/archiver/archiver_impl.rs | 7 ++- src/commands/backup.rs | 87 ++++++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/archiver/archiver_impl.rs b/src/archiver/archiver_impl.rs index b887257..aadfa2e 100644 --- a/src/archiver/archiver_impl.rs +++ b/src/archiver/archiver_impl.rs @@ -1,4 +1,5 @@ use std::fs::File; +use std::io::Read; use std::path::{Path, PathBuf}; use anyhow::{anyhow, Result}; @@ -221,7 +222,11 @@ impl Archiver { } } let f = File::open(path)?; - let chunk_iter = ChunkIter::new(f, *node.meta().size() as usize, &self.poly); + self.backup_reader(f, node, p).await + } + + pub async fn backup_reader(&mut self, r: impl Read, node: Node, p: ProgressBar) -> Result<()> { + let chunk_iter = ChunkIter::new(r, *node.meta().size() as usize, &self.poly); let mut content = Vec::new(); let mut filesize: u64 = 0; diff --git a/src/commands/backup.rs b/src/commands/backup.rs index eb2e5c3..d8d504f 100644 --- a/src/commands/backup.rs +++ b/src/commands/backup.rs @@ -13,6 +13,7 @@ use crate::backend::{ DecryptFullBackend, DecryptWriteBackend, DryRunBackend, LocalSource, LocalSourceOptions, ReadSource, }; +use crate::blob::{Metadata, Node, NodeType}; use crate::index::IndexBackend; use crate::repo::{ConfigFile, DeleteOption, SnapshotFile, SnapshotSummary, StringList}; @@ -51,10 +52,14 @@ pub(super) struct Opts { #[clap(long, value_name = "DURATION")] delete_after: Option, + /// Set filename to be used when backing up from stdin + #[clap(long, value_name = "FILENAME", default_value = "stdin")] + stdin_filename: String, + #[clap(flatten)] ignore_opts: LocalSourceOptions, - /// Backup source + /// Backup source, use - for stdin source: String, } @@ -69,8 +74,12 @@ pub(super) async fn execute( let mut be = DryRunBackend::new(be.clone(), opts.dry_run); be.set_zstd(zstd); - let backup_path = PathBuf::from(&opts.source); - let backup_path = backup_path.absolutize()?; + let backup_stdin = opts.source == "-"; + let backup_path = if backup_stdin { + PathBuf::from(&opts.stdin_filename) + } else { + PathBuf::from(&opts.source).absolutize()?.to_path_buf() + }; let backup_path_str = backup_path .to_str() .ok_or_else(|| anyhow!("non-unicode path {:?}", backup_path))? @@ -82,16 +91,16 @@ pub(super) async fn execute( .ok_or_else(|| anyhow!("non-unicode hostname {:?}", hostname))? .to_string(); - let parent = match (opts.force, opts.parent) { - (true, _) => None, - (false, None) => SnapshotFile::latest( + let parent = match (backup_stdin, opts.force, opts.parent) { + (true, _, _) | (false, true, _) => None, + (false, false, None) => SnapshotFile::latest( &be, |snap| snap.hostname == hostname && snap.paths.contains(&backup_path_str), progress_counter(), ) .await .ok(), - (false, Some(parent)) => SnapshotFile::from_id(&be, &parent).await.ok(), + (false, false, Some(parent)) => SnapshotFile::from_id(&be, &parent).await.ok(), }; let parent_tree = match &parent { @@ -129,32 +138,54 @@ pub(super) async fn execute( let parent = Parent::new(&index, parent_tree, opts.ignore_ctime, opts.ignore_inode).await; - let src = LocalSource::new(opts.ignore_opts, backup_path.to_path_buf())?; + let snap = if backup_stdin { + v1!("starting backup from stdin..."); + let mut archiver = Archiver::new(be, index, &config, parent, snap)?; + let p = progress_bytes(); + archiver + .backup_reader( + std::io::stdin(), + Node::new( + backup_path_str, + NodeType::File, + Metadata::default(), + None, + None, + ), + p, + ) + .await?; - let size = if get_verbosity_level() == 1 { - v1!("determining size of backup source..."); - src.size()? + archiver.finalize_snapshot().await? } else { - 0 - }; - v1!("starting backup..."); - let mut archiver = Archiver::new(be, index, &config, parent, snap)?; - let p = progress_bytes(); - p.set_length(size); - for item in src { - match item { - Err(e) => { - eprintln!("ignoring error {}\n", e) - } - Ok((path, node)) => { - if let Err(e) = archiver.add_entry(&path, node, p.clone()).await { - eprintln!("ignoring error {} for {:?}\n", e, path); + let src = LocalSource::new(opts.ignore_opts, backup_path)?; + + let size = if get_verbosity_level() == 1 { + v1!("determining size of backup source..."); + src.size()? + } else { + 0 + }; + v1!("starting backup..."); + let mut archiver = Archiver::new(be, index, &config, parent, snap)?; + let p = progress_bytes(); + p.set_length(size); + for item in src { + match item { + Err(e) => { + eprintln!("ignoring error {}\n", e) + } + Ok((path, node)) => { + if let Err(e) = archiver.add_entry(&path, node, p.clone()).await { + eprintln!("ignoring error {} for {:?}\n", e, path); + } } } } - } - p.finish_with_message("done"); - let snap = archiver.finalize_snapshot().await?; + p.finish_with_message("done"); + archiver.finalize_snapshot().await? + }; + let summary = snap.summary.unwrap(); v1!(