mirror of https://github.com/dswd/zvault
Backups
This commit is contained in:
parent
7e66d806b5
commit
1a2ea29d24
|
@ -0,0 +1,259 @@
|
||||||
|
++ mkdir repos
|
||||||
|
++ target/release/zvault init --compression brotli/3 repos/zvault_brotli3
|
||||||
|
|
||||||
|
real 0m0.009s
|
||||||
|
user 0m0.008s
|
||||||
|
sys 0m0.000s
|
||||||
|
++ target/release/zvault init --compression brotli/6 repos/zvault_brotli6
|
||||||
|
|
||||||
|
real 0m0.010s
|
||||||
|
user 0m0.004s
|
||||||
|
sys 0m0.004s
|
||||||
|
++ target/release/zvault init --compression lzma2/2 repos/zvault_lzma2
|
||||||
|
|
||||||
|
real 0m0.009s
|
||||||
|
user 0m0.004s
|
||||||
|
sys 0m0.004s
|
||||||
|
++ attic init repos/attic
|
||||||
|
Initializing repository at "repos/attic"
|
||||||
|
Encryption NOT enabled.
|
||||||
|
Use the "--encryption=passphrase|keyfile" to enable encryption.
|
||||||
|
Initializing cache...
|
||||||
|
|
||||||
|
real 0m0.136s
|
||||||
|
user 0m0.100s
|
||||||
|
sys 0m0.016s
|
||||||
|
++ borg init -e none repos/borg
|
||||||
|
|
||||||
|
real 0m0.253s
|
||||||
|
user 0m0.204s
|
||||||
|
sys 0m0.012s
|
||||||
|
++ borg init -e none repos/borg-zlib
|
||||||
|
|
||||||
|
real 0m0.243s
|
||||||
|
user 0m0.200s
|
||||||
|
sys 0m0.008s
|
||||||
|
++ zbackup init --non-encrypted repos/zbackup
|
||||||
|
|
||||||
|
real 0m0.003s
|
||||||
|
user 0m0.000s
|
||||||
|
sys 0m0.000s
|
||||||
|
++ cat
|
||||||
|
++ target/release/zvault put repos/zvault_brotli3::silesia1 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m3.389s
|
||||||
|
user 0m3.172s
|
||||||
|
sys 0m0.220s
|
||||||
|
++ target/release/zvault put repos/zvault_brotli3::silesia2 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m0.741s
|
||||||
|
user 0m0.708s
|
||||||
|
sys 0m0.032s
|
||||||
|
++ target/release/zvault put repos/zvault_brotli6::silesia1 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m10.166s
|
||||||
|
user 0m9.880s
|
||||||
|
sys 0m0.284s
|
||||||
|
++ target/release/zvault put repos/zvault_brotli6::silesia2 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m0.707s
|
||||||
|
user 0m0.660s
|
||||||
|
sys 0m0.044s
|
||||||
|
++ target/release/zvault put repos/zvault_lzma2::silesia1 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m26.277s
|
||||||
|
user 0m25.988s
|
||||||
|
sys 0m0.288s
|
||||||
|
++ target/release/zvault put repos/zvault_lzma2::silesia2 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m0.710s
|
||||||
|
user 0m0.656s
|
||||||
|
sys 0m0.052s
|
||||||
|
++ attic create repos/attic::silesia1 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m9.304s
|
||||||
|
user 0m8.440s
|
||||||
|
sys 0m0.328s
|
||||||
|
++ attic create repos/attic::silesia2 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m1.210s
|
||||||
|
user 0m1.128s
|
||||||
|
sys 0m0.040s
|
||||||
|
++ borg create -C none repos/borg::silesia1 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m4.805s
|
||||||
|
user 0m2.240s
|
||||||
|
sys 0m1.156s
|
||||||
|
++ borg create -C none repos/borg::silesia2 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m1.475s
|
||||||
|
user 0m1.260s
|
||||||
|
sys 0m0.164s
|
||||||
|
++ borg create -C zlib repos/borg-zlib::silesia1 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m10.093s
|
||||||
|
user 0m9.184s
|
||||||
|
sys 0m0.428s
|
||||||
|
++ borg create -C zlib repos/borg-zlib::silesia2 test_data/silesia.tar
|
||||||
|
|
||||||
|
real 0m1.534s
|
||||||
|
user 0m1.280s
|
||||||
|
sys 0m0.192s
|
||||||
|
++ zbackup backup --non-encrypted repos/zbackup/backups/silesia1
|
||||||
|
Loading index...
|
||||||
|
Index loaded.
|
||||||
|
Using up to 4 thread(s) for compression
|
||||||
|
|
||||||
|
real 0m24.362s
|
||||||
|
user 1m32.292s
|
||||||
|
sys 0m0.776s
|
||||||
|
++ zbackup backup --non-encrypted repos/zbackup/backups/silesia2
|
||||||
|
Loading index...
|
||||||
|
Loading index file 90cd1b771e3c068c6617b0089f4fe5fde4f654cb0473b7d7...
|
||||||
|
Index loaded.
|
||||||
|
Using up to 4 thread(s) for compression
|
||||||
|
|
||||||
|
real 0m1.281s
|
||||||
|
user 0m1.248s
|
||||||
|
sys 0m0.032s
|
||||||
|
++ du -h test_data/silesia.tar
|
||||||
|
203M test_data/silesia.tar
|
||||||
|
++ du -sh repos/zbackup/bundles repos/zvault_brotli3/bundles repos/zvault_brotli6/bundles repos/zvault_lzma2/bundles repos/attic repos/borg repos/borg-zlib repos/zbackup
|
||||||
|
51M repos/zbackup/bundles
|
||||||
|
65M repos/zvault_brotli3/bundles
|
||||||
|
58M repos/zvault_brotli6/bundles
|
||||||
|
55M repos/zvault_lzma2/bundles
|
||||||
|
68M repos/attic
|
||||||
|
203M repos/borg
|
||||||
|
66M repos/borg-zlib
|
||||||
|
164K repos/zbackup
|
||||||
|
++ rm -rf repos
|
||||||
|
++ mkdir repos
|
||||||
|
++ target/release/zvault init --compression brotli/3 repos/zvault_brotli3
|
||||||
|
|
||||||
|
real 0m0.005s
|
||||||
|
user 0m0.000s
|
||||||
|
sys 0m0.004s
|
||||||
|
++ target/release/zvault init --compression brotli/6 repos/zvault_brotli6
|
||||||
|
|
||||||
|
real 0m0.005s
|
||||||
|
user 0m0.004s
|
||||||
|
sys 0m0.000s
|
||||||
|
++ target/release/zvault init --compression lzma2/2 repos/zvault_lzma2
|
||||||
|
|
||||||
|
real 0m0.005s
|
||||||
|
user 0m0.004s
|
||||||
|
sys 0m0.000s
|
||||||
|
++ attic init repos/attic
|
||||||
|
Initializing repository at "repos/attic"
|
||||||
|
Encryption NOT enabled.
|
||||||
|
Use the "--encryption=passphrase|keyfile" to enable encryption.
|
||||||
|
Initializing cache...
|
||||||
|
|
||||||
|
real 0m0.095s
|
||||||
|
user 0m0.060s
|
||||||
|
sys 0m0.020s
|
||||||
|
++ borg init -e none repos/borg
|
||||||
|
|
||||||
|
real 0m0.235s
|
||||||
|
user 0m0.184s
|
||||||
|
sys 0m0.016s
|
||||||
|
++ borg init -e none repos/borg-zlib
|
||||||
|
|
||||||
|
real 0m0.262s
|
||||||
|
user 0m0.200s
|
||||||
|
sys 0m0.016s
|
||||||
|
++ zbackup init --non-encrypted repos/zbackup
|
||||||
|
|
||||||
|
real 0m0.004s
|
||||||
|
user 0m0.000s
|
||||||
|
sys 0m0.000s
|
||||||
|
++ cat
|
||||||
|
++ target/release/zvault put repos/zvault_brotli3::ubuntu1 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m3.008s
|
||||||
|
user 0m2.748s
|
||||||
|
sys 0m0.260s
|
||||||
|
++ target/release/zvault put repos/zvault_brotli3::ubuntu2 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m0.621s
|
||||||
|
user 0m0.580s
|
||||||
|
sys 0m0.040s
|
||||||
|
++ target/release/zvault put repos/zvault_brotli6::ubuntu1 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m10.916s
|
||||||
|
user 0m10.680s
|
||||||
|
sys 0m0.232s
|
||||||
|
++ target/release/zvault put repos/zvault_brotli6::ubuntu2 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m0.619s
|
||||||
|
user 0m0.596s
|
||||||
|
sys 0m0.020s
|
||||||
|
++ target/release/zvault put repos/zvault_lzma2::ubuntu1 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m37.039s
|
||||||
|
user 0m36.792s
|
||||||
|
sys 0m0.244s
|
||||||
|
++ target/release/zvault put repos/zvault_lzma2::ubuntu2 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m0.640s
|
||||||
|
user 0m0.624s
|
||||||
|
sys 0m0.012s
|
||||||
|
++ attic create repos/attic::ubuntu1 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m9.309s
|
||||||
|
user 0m8.316s
|
||||||
|
sys 0m0.368s
|
||||||
|
++ attic create repos/attic::ubuntu2 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m1.093s
|
||||||
|
user 0m1.008s
|
||||||
|
sys 0m0.044s
|
||||||
|
++ borg create -C none repos/borg::ubuntu1 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m4.317s
|
||||||
|
user 0m1.988s
|
||||||
|
sys 0m1.032s
|
||||||
|
++ borg create -C none repos/borg::ubuntu2 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m1.402s
|
||||||
|
user 0m1.160s
|
||||||
|
sys 0m0.188s
|
||||||
|
++ borg create -C zlib repos/borg-zlib::ubuntu1 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m10.049s
|
||||||
|
user 0m8.788s
|
||||||
|
sys 0m0.532s
|
||||||
|
++ borg create -C zlib repos/borg-zlib::ubuntu2 test_data/ubuntu.tar
|
||||||
|
|
||||||
|
real 0m1.291s
|
||||||
|
user 0m1.088s
|
||||||
|
sys 0m0.168s
|
||||||
|
++ zbackup backup --non-encrypted repos/zbackup/backups/ubuntu1
|
||||||
|
Loading index...
|
||||||
|
Index loaded.
|
||||||
|
Using up to 4 thread(s) for compression
|
||||||
|
|
||||||
|
real 0m17.972s
|
||||||
|
user 1m7.956s
|
||||||
|
sys 0m0.644s
|
||||||
|
++ zbackup backup --non-encrypted repos/zbackup/backups/ubuntu2
|
||||||
|
Loading index...
|
||||||
|
Loading index file 3fea916708827f3f5cdfc18fca024ef9cf00aa92a0c8d676...
|
||||||
|
Index loaded.
|
||||||
|
Using up to 4 thread(s) for compression
|
||||||
|
|
||||||
|
real 0m1.158s
|
||||||
|
user 0m1.132s
|
||||||
|
sys 0m0.024s
|
||||||
|
++ du -h test_data/ubuntu.tar
|
||||||
|
176M test_data/ubuntu.tar
|
||||||
|
++ du -sh repos/zbackup/bundles repos/zvault_brotli3/bundles repos/zvault_brotli6/bundles repos/zvault_lzma2/bundles repos/attic repos/borg repos/borg-zlib repos/zbackup
|
||||||
|
64M repos/zbackup/bundles
|
||||||
|
77M repos/zvault_brotli3/bundles
|
||||||
|
68M repos/zvault_brotli6/bundles
|
||||||
|
63M repos/zvault_lzma2/bundles
|
||||||
|
84M repos/attic
|
||||||
|
176M repos/borg
|
||||||
|
83M repos/borg-zlib
|
||||||
|
148K repos/zbackup
|
|
@ -483,6 +483,11 @@ impl Index {
|
||||||
self.entries
|
self.entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.mmap.len()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.entries == 0
|
self.entries == 0
|
||||||
|
|
92
src/main.rs
92
src/main.rs
|
@ -11,34 +11,38 @@ extern crate docopt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
mod util;
|
pub mod util;
|
||||||
mod bundle;
|
pub mod bundle;
|
||||||
mod index;
|
pub mod index;
|
||||||
mod chunker;
|
mod chunker;
|
||||||
mod repository;
|
mod repository;
|
||||||
mod algotest;
|
mod algotest;
|
||||||
|
|
||||||
use chunker::ChunkerType;
|
|
||||||
use repository::{Repository, Config, Mode, Inode};
|
|
||||||
use util::{ChecksumType, Compression, HashMethod, to_file_size};
|
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
|
|
||||||
|
use chunker::ChunkerType;
|
||||||
|
use repository::{Repository, Config, Mode, Inode, Backup};
|
||||||
|
use util::{ChecksumType, Compression, HashMethod, to_file_size};
|
||||||
|
|
||||||
|
|
||||||
static USAGE: &'static str = "
|
static USAGE: &'static str = "
|
||||||
Usage:
|
Usage:
|
||||||
zvault init <repo>
|
zvault init [--bundle-size SIZE] [--chunker METHOD] [--chunk-size SIZE] [--compression COMPRESSION] <repo>
|
||||||
zvault info <repo>
|
zvault backup [--full] <backup> <path>
|
||||||
zvault bundles <repo>
|
zvault restore <backup> <path>
|
||||||
zvault check [--full] <repo>
|
zvault check [--full] <repo>
|
||||||
|
zvault list <repo>
|
||||||
|
zvault info <backup>
|
||||||
|
zvault stats <repo>
|
||||||
|
zvault bundles <repo>
|
||||||
zvault algotest <path>
|
zvault algotest <path>
|
||||||
zvault test <repo> <path>
|
zvault test <repo> <path>
|
||||||
zvault stat <path>
|
zvault stat <path>
|
||||||
zvault put <repo> <path>
|
zvault put <backup> <path>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--full Whether to verify the repository by loading all bundles
|
--full Whether to verify the repository by loading all bundles
|
||||||
|
@ -52,15 +56,25 @@ Options:
|
||||||
#[derive(RustcDecodable, Debug)]
|
#[derive(RustcDecodable, Debug)]
|
||||||
struct Args {
|
struct Args {
|
||||||
cmd_init: bool,
|
cmd_init: bool,
|
||||||
|
cmd_backup: bool,
|
||||||
|
cmd_restore: bool,
|
||||||
|
cmd_check: bool,
|
||||||
|
|
||||||
|
cmd_list: bool,
|
||||||
cmd_info: bool,
|
cmd_info: bool,
|
||||||
|
|
||||||
|
cmd_stats: bool,
|
||||||
|
cmd_bundles: bool,
|
||||||
|
|
||||||
cmd_algotest: bool,
|
cmd_algotest: bool,
|
||||||
cmd_test: bool,
|
cmd_test: bool,
|
||||||
cmd_stat: bool,
|
cmd_stat: bool,
|
||||||
cmd_check: bool,
|
|
||||||
cmd_bundles: bool,
|
|
||||||
cmd_put: bool,
|
cmd_put: bool,
|
||||||
|
|
||||||
arg_repo: Option<String>,
|
arg_repo: Option<String>,
|
||||||
arg_path: Option<String>,
|
arg_path: Option<String>,
|
||||||
|
arg_backup: Option<String>,
|
||||||
|
|
||||||
flag_full: bool,
|
flag_full: bool,
|
||||||
flag_bundle_size: usize,
|
flag_bundle_size: usize,
|
||||||
flag_chunker: String,
|
flag_chunker: String,
|
||||||
|
@ -100,14 +114,23 @@ fn main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut repo = Repository::open(&args.arg_repo.unwrap()).unwrap();
|
|
||||||
|
let mut repo;
|
||||||
|
if let Some(path) = args.arg_repo {
|
||||||
|
repo = Repository::open(path).unwrap();
|
||||||
|
} else if let Some(ref backup) = args.arg_backup {
|
||||||
|
let path = backup.splitn(2, "::").nth(0).unwrap();
|
||||||
|
repo = Repository::open(path).unwrap();
|
||||||
|
} else {
|
||||||
|
panic!("Repository is needed");
|
||||||
|
}
|
||||||
|
|
||||||
if args.cmd_check {
|
if args.cmd_check {
|
||||||
repo.check(args.flag_full).unwrap();
|
repo.check(args.flag_full).unwrap();
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.cmd_info {
|
if args.cmd_stats {
|
||||||
let info = repo.info();
|
let info = repo.info();
|
||||||
println!("Bundles: {}", info.bundle_count);
|
println!("Bundles: {}", info.bundle_count);
|
||||||
println!("Total size: {}", to_file_size(info.encoded_data_size));
|
println!("Total size: {}", to_file_size(info.encoded_data_size));
|
||||||
|
@ -115,6 +138,15 @@ fn main() {
|
||||||
println!("Compression ratio: {:.1}", info.compression_ratio * 100.0);
|
println!("Compression ratio: {:.1}", info.compression_ratio * 100.0);
|
||||||
println!("Chunk count: {}", info.chunk_count);
|
println!("Chunk count: {}", info.chunk_count);
|
||||||
println!("Average chunk size: {}", to_file_size(info.avg_chunk_size as u64));
|
println!("Average chunk size: {}", to_file_size(info.avg_chunk_size as u64));
|
||||||
|
let index_usage = info.index_entries as f32 / info.index_capacity as f32;
|
||||||
|
println!("Index: {}, {}% full", to_file_size(info.index_size as u64), index_usage * 100.0);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.cmd_list {
|
||||||
|
for backup in repo.list_backups().unwrap() {
|
||||||
|
println!("{}", backup);
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,12 +168,6 @@ fn main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.cmd_put {
|
|
||||||
let chunks = repo.put_inode(&args.arg_path.unwrap()).unwrap();
|
|
||||||
println!("done. {} chunks, total size: {}", chunks.len(), to_file_size(chunks.iter().map(|&(_,s)| s).sum::<usize>() as u64));
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.cmd_test {
|
if args.cmd_test {
|
||||||
print!("Integrity check before...");
|
print!("Integrity check before...");
|
||||||
repo.check(true).unwrap();
|
repo.check(true).unwrap();
|
||||||
|
@ -175,5 +201,29 @@ fn main() {
|
||||||
let read_speed = data.len() as f64 / duration;
|
let read_speed = data.len() as f64 / duration;
|
||||||
assert_eq!(data.len(), data2.len());
|
assert_eq!(data.len(), data2.len());
|
||||||
println!(" done. {:.1} MB/s", read_speed / 1_000_000.0);
|
println!(" done. {:.1} MB/s", read_speed / 1_000_000.0);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let backup_name = args.arg_backup.unwrap().splitn(2, "::").nth(1).unwrap().to_string();
|
||||||
|
|
||||||
|
if args.cmd_put {
|
||||||
|
let chunks = repo.put_inode(&args.arg_path.unwrap()).unwrap();
|
||||||
|
repo.save_backup(&Backup{root: chunks, ..Default::default()}, &backup_name).unwrap();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.cmd_backup {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
let backup = repo.get_backup(&backup_name).unwrap();
|
||||||
|
|
||||||
|
if args.cmd_info {
|
||||||
|
println!("{:?}", backup.root);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.cmd_restore {
|
||||||
|
repo.restore_backup(&backup, &args.arg_path.unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
use super::{Repository, Chunk};
|
||||||
|
|
||||||
|
use rmp_serde;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Backup {
|
||||||
|
pub root: Vec<Chunk>,
|
||||||
|
pub total_data_size: u64,
|
||||||
|
pub changed_data_size: u64,
|
||||||
|
pub new_data_size: u64,
|
||||||
|
pub encoded_data_size: u64,
|
||||||
|
pub new_bundle_count: usize,
|
||||||
|
pub chunk_count: usize,
|
||||||
|
pub avg_chunk_size: f32,
|
||||||
|
pub date: i64,
|
||||||
|
pub duration: f32,
|
||||||
|
pub file_count: usize,
|
||||||
|
pub dir_count: usize
|
||||||
|
}
|
||||||
|
serde_impl!(Backup(u8) {
|
||||||
|
root: Vec<Chunk> => 0,
|
||||||
|
total_data_size: u64 => 1,
|
||||||
|
changed_data_size: u64 => 2,
|
||||||
|
new_data_size: u64 => 3,
|
||||||
|
encoded_data_size: u64 => 4,
|
||||||
|
new_bundle_count: usize => 5,
|
||||||
|
chunk_count: usize => 6,
|
||||||
|
avg_chunk_size: f32 => 7,
|
||||||
|
date: i64 => 8,
|
||||||
|
duration: f32 => 9,
|
||||||
|
file_count: usize => 10,
|
||||||
|
dir_count: usize => 11
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
impl Repository {
|
||||||
|
pub fn list_backups(&self) -> Result<Vec<String>, &'static str> {
|
||||||
|
let mut backups = Vec::new();
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
let base_path = self.path.join("backups");
|
||||||
|
paths.push(base_path.clone());
|
||||||
|
while let Some(path) = paths.pop() {
|
||||||
|
for entry in try!(fs::read_dir(path).map_err(|_| "Failed to list files")) {
|
||||||
|
let entry = try!(entry.map_err(|_| "Failed to list files"));
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
paths.push(path);
|
||||||
|
} else {
|
||||||
|
let relpath = try!(path.strip_prefix(&base_path).map_err(|_| "Failed to obtain relative path"));
|
||||||
|
backups.push(relpath.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(backups)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_backup(&self, name: &str) -> Result<Backup, &'static str> {
|
||||||
|
let file = try!(File::open(self.path.join("backups").join(name)).map_err(|_| "Failed to load backup"));
|
||||||
|
let mut reader = rmp_serde::Deserializer::new(file);
|
||||||
|
Backup::deserialize(&mut reader).map_err(|_| "Failed to read backup data")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_backup(&mut self, backup: &Backup, name: &str) -> Result<(), &'static str> {
|
||||||
|
let mut file = try!(File::create(self.path.join("backups").join(name)).map_err(|_| "Failed to save backup"));
|
||||||
|
let mut writer = rmp_serde::Serializer::new(&mut file);
|
||||||
|
backup.serialize(&mut writer).map_err(|_| "Failed to write backup data")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_backup<P: AsRef<Path>>(&mut self, backup: &Backup, path: P) -> Result<(), &'static str> {
|
||||||
|
let inode = try!(self.get_inode(&backup.root));
|
||||||
|
try!(self.save_inode_at(&inode, path));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,10 @@ pub struct RepositoryInfo {
|
||||||
pub raw_data_size: u64,
|
pub raw_data_size: u64,
|
||||||
pub compression_ratio: f32,
|
pub compression_ratio: f32,
|
||||||
pub chunk_count: usize,
|
pub chunk_count: usize,
|
||||||
pub avg_chunk_size: f32
|
pub avg_chunk_size: f32,
|
||||||
|
pub index_size: usize,
|
||||||
|
pub index_capacity: usize,
|
||||||
|
pub index_entries: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +31,10 @@ impl Repository {
|
||||||
encoded_data_size: encoded_data_size,
|
encoded_data_size: encoded_data_size,
|
||||||
raw_data_size: raw_data_size,
|
raw_data_size: raw_data_size,
|
||||||
compression_ratio: encoded_data_size as f32 / raw_data_size as f32,
|
compression_ratio: encoded_data_size as f32 / raw_data_size as f32,
|
||||||
avg_chunk_size: raw_data_size as f32 / chunk_count as f32
|
avg_chunk_size: raw_data_size as f32 / chunk_count as f32,
|
||||||
|
index_size: self.index.size(),
|
||||||
|
index_capacity: self.index.capacity(),
|
||||||
|
index_entries: self.index.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ use rmp_serde;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::{self, Metadata, File};
|
use std::fs::{self, Metadata, File, Permissions};
|
||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
use std::io::{Cursor, Read};
|
use std::os::unix::fs::{PermissionsExt, symlink};
|
||||||
|
use std::io::{Cursor, Read, Write};
|
||||||
|
|
||||||
use ::util::Hash;
|
|
||||||
use super::{Repository, Mode, Chunk};
|
use super::{Repository, Mode, Chunk};
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,25 +118,29 @@ impl Inode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn create_at<P: AsRef<Path>>(&self, path: P) -> Result<(), &'static str> {
|
pub fn create_at<P: AsRef<Path>>(&self, path: P) -> Result<Option<File>, &'static str> {
|
||||||
let full_path = path.as_ref().join(&self.name);
|
let full_path = path.as_ref().join(&self.name);
|
||||||
|
let mut file = None;
|
||||||
match self.file_type {
|
match self.file_type {
|
||||||
FileType::File => {
|
FileType::File => {
|
||||||
try!(File::create(&full_path).map_err(|_| "Failed to create file"));
|
file = Some(try!(File::create(&full_path).map_err(|_| "Failed to create file")));
|
||||||
},
|
},
|
||||||
FileType::Directory => {
|
FileType::Directory => {
|
||||||
try!(fs::create_dir(&full_path).map_err(|_| "Failed to create directory"));
|
try!(fs::create_dir(&full_path).map_err(|_| "Failed to create directory"));
|
||||||
},
|
},
|
||||||
FileType::Symlink => {
|
FileType::Symlink => {
|
||||||
if let Some(ref src) = self.symlink_target {
|
if let Some(ref src) = self.symlink_target {
|
||||||
try!(fs::soft_link(src, &full_path).map_err(|_| "Failed to create symlink"));
|
|
||||||
|
try!(symlink(src, &full_path).map_err(|_| "Failed to create symlink"));
|
||||||
} else {
|
} else {
|
||||||
return Err("Symlink without destination")
|
return Err("Symlink without destination")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//FIXME: set times and permissions
|
try!(fs::set_permissions(&full_path, Permissions::from_mode(self.mode)).map_err(|_| "Failed to set permissions"));
|
||||||
Ok(())
|
//FIXME: set times and gid/uid
|
||||||
|
// https://crates.io/crates/filetime
|
||||||
|
Ok(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +162,7 @@ impl Repository {
|
||||||
let mut inode_data = Vec::new();
|
let mut inode_data = Vec::new();
|
||||||
{
|
{
|
||||||
let mut writer = rmp_serde::Serializer::new(&mut inode_data);
|
let mut writer = rmp_serde::Serializer::new(&mut inode_data);
|
||||||
inode.serialize(&mut writer).map_err(|_| "Failed to write inode data");
|
try!(inode.serialize(&mut writer).map_err(|_| "Failed to write inode data"));
|
||||||
}
|
}
|
||||||
self.put_data(Mode::Meta, &inode_data)
|
self.put_data(Mode::Meta, &inode_data)
|
||||||
}
|
}
|
||||||
|
@ -169,4 +173,21 @@ impl Repository {
|
||||||
let mut reader = rmp_serde::Deserializer::new(data);
|
let mut reader = rmp_serde::Deserializer::new(data);
|
||||||
Inode::deserialize(&mut reader).map_err(|_| "Failed to read inode data")
|
Inode::deserialize(&mut reader).map_err(|_| "Failed to read inode data")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn save_inode_at<P: AsRef<Path>>(&mut self, inode: &Inode, path: P) -> Result<(), &'static str> {
|
||||||
|
if let Some(mut file) = try!(inode.create_at(path.as_ref())) {
|
||||||
|
if let Some(ref contents) = inode.contents {
|
||||||
|
match *contents {
|
||||||
|
FileContents::Inline(ref data) => {
|
||||||
|
try!(file.write_all(&data).map_err(|_| "Failed to write data to file"));
|
||||||
|
},
|
||||||
|
FileContents::Chunked(ref chunks) => {
|
||||||
|
try!(self.get_stream(chunks, &mut file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ mod integrity;
|
||||||
mod basic_io;
|
mod basic_io;
|
||||||
mod info;
|
mod info;
|
||||||
mod metadata;
|
mod metadata;
|
||||||
|
mod backup;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
@ -17,6 +18,7 @@ use super::chunker::Chunker;
|
||||||
pub use self::config::Config;
|
pub use self::config::Config;
|
||||||
pub use self::metadata::Inode;
|
pub use self::metadata::Inode;
|
||||||
pub use self::basic_io::Chunk;
|
pub use self::basic_io::Chunk;
|
||||||
|
pub use self::backup::Backup;
|
||||||
use self::bundle_map::BundleMap;
|
use self::bundle_map::BundleMap;
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +55,7 @@ impl Repository {
|
||||||
try!(config.save(path.join("config.yaml")).map_err(|_| "Failed to save config"));
|
try!(config.save(path.join("config.yaml")).map_err(|_| "Failed to save config"));
|
||||||
let bundle_map = BundleMap::create();
|
let bundle_map = BundleMap::create();
|
||||||
try!(bundle_map.save(path.join("bundles.map")).map_err(|_| "Failed to save bundle map"));
|
try!(bundle_map.save(path.join("bundles.map")).map_err(|_| "Failed to save bundle map"));
|
||||||
|
try!(fs::create_dir(&path.join("backups")).map_err(|_| "Failed to create backup directory"));
|
||||||
Ok(Repository{
|
Ok(Repository{
|
||||||
path: path,
|
path: path,
|
||||||
chunker: config.chunker.create(),
|
chunker: config.chunker.create(),
|
||||||
|
|
|
@ -33,14 +33,14 @@ impl Hash {
|
||||||
impl fmt::Display for Hash {
|
impl fmt::Display for Hash {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(fmt, "{:16x}{:16x}", self.high, self.low)
|
write!(fmt, "{:016x}{:016x}", self.high, self.low)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Hash {
|
impl fmt::Debug for Hash {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(fmt, "{:16x}{:16x}", self.high, self.low)
|
write!(fmt, "{:016x}{:016x}", self.high, self.low)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,9 +45,9 @@ impl<K: Eq+Hash, V> LruCache<K, V> {
|
||||||
fn shrink(&mut self) {
|
fn shrink(&mut self) {
|
||||||
let mut tags: Vec<u64> = self.items.values().map(|&(_, n)| n).collect();
|
let mut tags: Vec<u64> = self.items.values().map(|&(_, n)| n).collect();
|
||||||
tags.sort();
|
tags.sort();
|
||||||
let bar = tags[tags.len()-self.min_size];
|
let min = tags[tags.len()-self.min_size];
|
||||||
let mut new = HashMap::with_capacity(self.min_size);
|
let mut new = HashMap::with_capacity(self.min_size);
|
||||||
new.extend(self.items.drain().filter(|&(_,(_, n))| n>=bar));
|
new.extend(self.items.drain().filter(|&(_,(_, n))| n>=min));
|
||||||
self.items = new;
|
self.items = new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
mkdir repos
|
||||||
|
time target/release/zvault init --compression brotli/3 repos/zvault_brotli3
|
||||||
|
time target/release/zvault init --compression brotli/6 repos/zvault_brotli6
|
||||||
|
time target/release/zvault init --compression lzma2/2 repos/zvault_lzma2
|
||||||
|
time attic init repos/attic
|
||||||
|
time borg init -e none repos/borg
|
||||||
|
time borg init -e none repos/borg-zlib
|
||||||
|
time zbackup init --non-encrypted repos/zbackup
|
||||||
|
|
||||||
|
cat < test_data/silesia.tar > /dev/null
|
||||||
|
time target/release/zvault put repos/zvault_brotli3::silesia1 test_data/silesia.tar
|
||||||
|
time target/release/zvault put repos/zvault_brotli3::silesia2 test_data/silesia.tar
|
||||||
|
time target/release/zvault put repos/zvault_brotli6::silesia1 test_data/silesia.tar
|
||||||
|
time target/release/zvault put repos/zvault_brotli6::silesia2 test_data/silesia.tar
|
||||||
|
time target/release/zvault put repos/zvault_lzma2::silesia1 test_data/silesia.tar
|
||||||
|
time target/release/zvault put repos/zvault_lzma2::silesia2 test_data/silesia.tar
|
||||||
|
time attic create repos/attic::silesia1 test_data/silesia.tar
|
||||||
|
time attic create repos/attic::silesia2 test_data/silesia.tar
|
||||||
|
time borg create -C none repos/borg::silesia1 test_data/silesia.tar
|
||||||
|
time borg create -C none repos/borg::silesia2 test_data/silesia.tar
|
||||||
|
time borg create -C zlib repos/borg-zlib::silesia1 test_data/silesia.tar
|
||||||
|
time borg create -C zlib repos/borg-zlib::silesia2 test_data/silesia.tar
|
||||||
|
time zbackup backup --non-encrypted repos/zbackup/backups/silesia1 < test_data/silesia.tar
|
||||||
|
time zbackup backup --non-encrypted repos/zbackup/backups/silesia2 < test_data/silesia.tar
|
||||||
|
|
||||||
|
du -h test_data/silesia.tar
|
||||||
|
du -sh repos/zvault*/bundles repos/attic repos/borg repos/borg-zlib repos/zbackup
|
||||||
|
|
||||||
|
rm -rf repos
|
||||||
|
|
||||||
|
|
||||||
|
mkdir repos
|
||||||
|
time target/release/zvault init --compression brotli/3 repos/zvault_brotli3
|
||||||
|
time target/release/zvault init --compression brotli/6 repos/zvault_brotli6
|
||||||
|
time target/release/zvault init --compression lzma2/2 repos/zvault_lzma2
|
||||||
|
time attic init repos/attic
|
||||||
|
time borg init -e none repos/borg
|
||||||
|
time borg init -e none repos/borg-zlib
|
||||||
|
time zbackup init --non-encrypted repos/zbackup
|
||||||
|
|
||||||
|
cat < test_data/ubuntu.tar > /dev/null
|
||||||
|
time target/release/zvault put repos/zvault_brotli3::ubuntu1 test_data/ubuntu.tar
|
||||||
|
time target/release/zvault put repos/zvault_brotli3::ubuntu2 test_data/ubuntu.tar
|
||||||
|
time target/release/zvault put repos/zvault_brotli6::ubuntu1 test_data/ubuntu.tar
|
||||||
|
time target/release/zvault put repos/zvault_brotli6::ubuntu2 test_data/ubuntu.tar
|
||||||
|
time target/release/zvault put repos/zvault_lzma2::ubuntu1 test_data/ubuntu.tar
|
||||||
|
time target/release/zvault put repos/zvault_lzma2::ubuntu2 test_data/ubuntu.tar
|
||||||
|
time attic create repos/attic::ubuntu1 test_data/ubuntu.tar
|
||||||
|
time attic create repos/attic::ubuntu2 test_data/ubuntu.tar
|
||||||
|
time borg create -C none repos/borg::ubuntu1 test_data/ubuntu.tar
|
||||||
|
time borg create -C none repos/borg::ubuntu2 test_data/ubuntu.tar
|
||||||
|
time borg create -C zlib repos/borg-zlib::ubuntu1 test_data/ubuntu.tar
|
||||||
|
time borg create -C zlib repos/borg-zlib::ubuntu2 test_data/ubuntu.tar
|
||||||
|
time zbackup backup --non-encrypted repos/zbackup/backups/ubuntu1 < test_data/ubuntu.tar
|
||||||
|
time zbackup backup --non-encrypted repos/zbackup/backups/ubuntu2 < test_data/ubuntu.tar
|
||||||
|
|
||||||
|
du -h test_data/ubuntu.tar
|
||||||
|
du -sh repos/zvault*/bundles repos/attic repos/borg repos/borg-zlib repos/zbackup
|
Loading…
Reference in New Issue