diff --git a/CHANGELOG.md b/CHANGELOG.md index e4272c1..5f0759c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ This project follows [semantic versioning](http://semver.org). ### UNRELEASED +* [added] Ability to read/write tar file from/to stdin/stdout +* [modified] Logging to stderr * [fixed] Only print "repairing bundles" if actually repairing bundles diff --git a/docs/man/zvault-backup.1.md b/docs/man/zvault-backup.1.md index 5da5868..a1d6e2c 100644 --- a/docs/man/zvault-backup.1.md +++ b/docs/man/zvault-backup.1.md @@ -29,6 +29,8 @@ The patterns can be given directly via `--exclude` or be read from a file via exclude pattern is read from the file `excludes` in the repository folder. All exclude pattern given via any of these ways will be combined. +If `--tar` is specified and `SRC` is `-`, the input is read from stdin. + Unless `--xdev` is set, zVault will not traverse into subfolders that are on a different filesystem, i.e. mount points will not be included. diff --git a/docs/man/zvault-restore.1.md b/docs/man/zvault-restore.1.md index 37fb1f9..bc8549a 100644 --- a/docs/man/zvault-restore.1.md +++ b/docs/man/zvault-restore.1.md @@ -16,7 +16,7 @@ The backup or backup subtree given by `BACKUP` must be in the format If `repository` is omitted, the default repository location is used instead. If `--tar` is set, the data is written to a tar file named `DST`. In this case -`DST` must not exist. +`DST` must not exist. If `DST` is `-`, the data will be written to stdout. If `--tar` is not set, the data will be written into the existing folder `DST`. diff --git a/src/cli/logger.rs b/src/cli/logger.rs index e39dff8..26e09fa 100644 --- a/src/cli/logger.rs +++ b/src/cli/logger.rs @@ -2,8 +2,16 @@ use log::{self, LogRecord, LogLevel, LogMetadata}; pub use log::SetLoggerError; use ansi_term::{Color, Style}; +use std::io::Write; +macro_rules! println_stderr( + ($($arg:tt)*) => { { + let r = writeln!(&mut ::std::io::stderr(), $($arg)*); + r.expect("failed printing to stderr"); + } } +); + struct Logger(LogLevel); impl log::Log for Logger { @@ -14,11 +22,11 @@ impl log::Log for Logger { fn log(&self, record: &LogRecord) { if self.enabled(record.metadata()) { match record.level() { - LogLevel::Error => println!("{}: {}", Color::Red.bold().paint("error"), record.args()), - LogLevel::Warn => println!("{}: {}", Color::Yellow.bold().paint("warning"), record.args()), - LogLevel::Info => println!("{}: {}", Color::Green.bold().paint("info"), record.args()), - LogLevel::Debug => println!("{}: {}", Style::new().bold().paint("debug"), record.args()), - LogLevel::Trace => println!("{}: {}", "trace", record.args()) + LogLevel::Error => println_stderr!("{}: {}", Color::Red.bold().paint("error"), record.args()), + LogLevel::Warn => println_stderr!("{}: {}", Color::Yellow.bold().paint("warning"), record.args()), + LogLevel::Info => println_stderr!("{}: {}", Color::Green.bold().paint("info"), record.args()), + LogLevel::Debug => println_stderr!("{}: {}", Style::new().bold().paint("debug"), record.args()), + LogLevel::Trace => println_stderr!("{}: {}", "trace", record.args()) } } } diff --git a/src/repository/tarfile.rs b/src/repository/tarfile.rs index 5eb6f00..4c9a12a 100644 --- a/src/repository/tarfile.rs +++ b/src/repository/tarfile.rs @@ -2,7 +2,7 @@ use ::prelude::*; use std::collections::{HashMap, HashSet, BTreeMap}; use std::path::{Path, PathBuf}; -use std::io::{Read, Cursor}; +use std::io::{self, Read, Write, Cursor}; use std::fs::File; use std::str; @@ -71,8 +71,8 @@ impl Repository { Ok(inode) } - fn import_tarfile_as_inode>(&mut self, backup: &mut Backup, tarfile: P, failed_paths: &mut Vec) -> Result<(Inode, ChunkList), RepositoryError> { - let mut tarfile = tar::Archive::new(try!(File::open(tarfile))); + fn import_tarfile_as_inode(&mut self, backup: &mut Backup, input: R, failed_paths: &mut Vec) -> Result<(Inode, ChunkList), RepositoryError> { + let mut tarfile = tar::Archive::new(input); // Step 1: create inodes for all entries let mut inodes = HashMap::)>::new(); for entry in try!(tarfile.entries()) { @@ -185,7 +185,12 @@ impl Repository { let info_before = self.info(); let start = Local::now(); let mut failed_paths = vec![]; - let (root_inode, chunks) = try!(self.import_tarfile_as_inode(&mut backup, tarfile, &mut failed_paths)); + let tarfile = tarfile.as_ref(); + let (root_inode, chunks) = if tarfile == Path::new("-") { + try!(self.import_tarfile_as_inode(&mut backup, try!(File::open(tarfile)), &mut failed_paths)) + } else { + try!(self.import_tarfile_as_inode(&mut backup, io::stdin(), &mut failed_paths)) + }; backup.root = chunks; try!(self.flush()); let elapsed = Local::now().signed_duration_since(start); @@ -208,7 +213,7 @@ impl Repository { } } - fn export_xattrs(&mut self, inode: &Inode, tarfile: &mut tar::Builder) -> Result<(), RepositoryError> { + fn export_xattrs(&mut self, inode: &Inode, tarfile: &mut tar::Builder) -> Result<(), RepositoryError> { let mut data = Vec::new(); for (key, value) in &inode.xattrs { let mut len_len = 1; @@ -231,7 +236,7 @@ impl Repository { Ok(try!(tarfile.append(&header, Cursor::new(&data)))) } - fn export_tarfile_recurse(&mut self, backup: &Backup, path: &Path, inode: Inode, tarfile: &mut tar::Builder) -> Result<(), RepositoryError> { + fn export_tarfile_recurse(&mut self, backup: &Backup, path: &Path, inode: Inode, tarfile: &mut tar::Builder) -> Result<(), RepositoryError> { if !inode.xattrs.is_empty() { try!(self.export_xattrs(&inode, tarfile)); } @@ -277,9 +282,16 @@ impl Repository { } pub fn export_tarfile>(&mut self, backup: &Backup, inode: Inode, tarfile: P) -> Result<(), RepositoryError> { - let mut tarfile = tar::Builder::new(try!(File::create(tarfile))); - try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile)); - try!(tarfile.finish()); + let tarfile = tarfile.as_ref(); + if tarfile == Path::new("-") { + let mut tarfile = tar::Builder::new(io::stdout()); + try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile)); + try!(tarfile.finish()); + } else { + let mut tarfile = tar::Builder::new(try!(File::create(tarfile))); + try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile)); + try!(tarfile.finish()); + } Ok(()) }