mirror of https://github.com/dswd/zvault
Allow stdin/stdout for --tar (closes #12)
This commit is contained in:
parent
12b00b13ec
commit
d06149979a
|
@ -4,6 +4,8 @@ This project follows [semantic versioning](http://semver.org).
|
||||||
|
|
||||||
|
|
||||||
### UNRELEASED
|
### 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
|
* [fixed] Only print "repairing bundles" if actually repairing bundles
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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.
|
exclude pattern is read from the file `excludes` in the repository folder.
|
||||||
All exclude pattern given via any of these ways will be combined.
|
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
|
Unless `--xdev` is set, zVault will not traverse into subfolders that are on a
|
||||||
different filesystem, i.e. mount points will not be included.
|
different filesystem, i.e. mount points will not be included.
|
||||||
|
|
||||||
|
|
|
@ -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 `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
|
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`.
|
If `--tar` is not set, the data will be written into the existing folder `DST`.
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,16 @@ use log::{self, LogRecord, LogLevel, LogMetadata};
|
||||||
pub use log::SetLoggerError;
|
pub use log::SetLoggerError;
|
||||||
|
|
||||||
use ansi_term::{Color, Style};
|
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);
|
struct Logger(LogLevel);
|
||||||
|
|
||||||
impl log::Log for Logger {
|
impl log::Log for Logger {
|
||||||
|
@ -14,11 +22,11 @@ impl log::Log for Logger {
|
||||||
fn log(&self, record: &LogRecord) {
|
fn log(&self, record: &LogRecord) {
|
||||||
if self.enabled(record.metadata()) {
|
if self.enabled(record.metadata()) {
|
||||||
match record.level() {
|
match record.level() {
|
||||||
LogLevel::Error => println!("{}: {}", Color::Red.bold().paint("error"), record.args()),
|
LogLevel::Error => println_stderr!("{}: {}", Color::Red.bold().paint("error"), record.args()),
|
||||||
LogLevel::Warn => println!("{}: {}", Color::Yellow.bold().paint("warning"), record.args()),
|
LogLevel::Warn => println_stderr!("{}: {}", Color::Yellow.bold().paint("warning"), record.args()),
|
||||||
LogLevel::Info => println!("{}: {}", Color::Green.bold().paint("info"), record.args()),
|
LogLevel::Info => println_stderr!("{}: {}", Color::Green.bold().paint("info"), record.args()),
|
||||||
LogLevel::Debug => println!("{}: {}", Style::new().bold().paint("debug"), record.args()),
|
LogLevel::Debug => println_stderr!("{}: {}", Style::new().bold().paint("debug"), record.args()),
|
||||||
LogLevel::Trace => println!("{}: {}", "trace", record.args())
|
LogLevel::Trace => println_stderr!("{}: {}", "trace", record.args())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use ::prelude::*;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet, BTreeMap};
|
use std::collections::{HashMap, HashSet, BTreeMap};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::io::{Read, Cursor};
|
use std::io::{self, Read, Write, Cursor};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
@ -71,8 +71,8 @@ impl Repository {
|
||||||
Ok(inode)
|
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> {
|
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(try!(File::open(tarfile)));
|
let mut tarfile = tar::Archive::new(input);
|
||||||
// Step 1: create inodes for all entries
|
// Step 1: create inodes for all entries
|
||||||
let mut inodes = HashMap::<PathBuf, (Inode, HashSet<String>)>::new();
|
let mut inodes = HashMap::<PathBuf, (Inode, HashSet<String>)>::new();
|
||||||
for entry in try!(tarfile.entries()) {
|
for entry in try!(tarfile.entries()) {
|
||||||
|
@ -185,7 +185,12 @@ impl Repository {
|
||||||
let info_before = self.info();
|
let info_before = self.info();
|
||||||
let start = Local::now();
|
let start = Local::now();
|
||||||
let mut failed_paths = vec![];
|
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;
|
backup.root = chunks;
|
||||||
try!(self.flush());
|
try!(self.flush());
|
||||||
let elapsed = Local::now().signed_duration_since(start);
|
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();
|
let mut data = Vec::new();
|
||||||
for (key, value) in &inode.xattrs {
|
for (key, value) in &inode.xattrs {
|
||||||
let mut len_len = 1;
|
let mut len_len = 1;
|
||||||
|
@ -231,7 +236,7 @@ impl Repository {
|
||||||
Ok(try!(tarfile.append(&header, Cursor::new(&data))))
|
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() {
|
if !inode.xattrs.is_empty() {
|
||||||
try!(self.export_xattrs(&inode, tarfile));
|
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> {
|
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)));
|
let tarfile = tarfile.as_ref();
|
||||||
try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile));
|
if tarfile == Path::new("-") {
|
||||||
try!(tarfile.finish());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue