Allow stdin/stdout for --tar (closes #12)

pull/10/head
Dennis Schwerdel 2017-04-14 22:39:32 +02:00
parent 12b00b13ec
commit d06149979a
5 changed files with 39 additions and 15 deletions

View File

@ -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

View File

@ -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.

View File

@ -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`.

View File

@ -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())
}
}
}

View File

@ -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<P: AsRef<Path>>(&mut self, backup: &mut Backup, tarfile: P, failed_paths: &mut Vec<PathBuf>) -> Result<(Inode, ChunkList), RepositoryError> {
let mut tarfile = tar::Archive::new(try!(File::open(tarfile)));
fn import_tarfile_as_inode<R: Read>(&mut self, backup: &mut Backup, input: R, failed_paths: &mut Vec<PathBuf>) -> Result<(Inode, ChunkList), RepositoryError> {
let mut tarfile = tar::Archive::new(input);
// Step 1: create inodes for all entries
let mut inodes = HashMap::<PathBuf, (Inode, HashSet<String>)>::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<File>) -> Result<(), RepositoryError> {
fn export_xattrs<W: Write>(&mut self, inode: &Inode, tarfile: &mut tar::Builder<W>) -> 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<File>) -> Result<(), RepositoryError> {
fn export_tarfile_recurse<W: Write>(&mut self, backup: &Backup, path: &Path, inode: Inode, tarfile: &mut tar::Builder<W>) -> Result<(), RepositoryError> {
if !inode.xattrs.is_empty() {
try!(self.export_xattrs(&inode, tarfile));
}
@ -277,9 +282,16 @@ impl Repository {
}
pub fn export_tarfile<P: AsRef<Path>>(&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(())
}