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
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
self.mmap.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.entries == 0
|
||||
|
|
102
src/main.rs
102
src/main.rs
|
@ -11,56 +11,70 @@ extern crate docopt;
|
|||
extern crate rustc_serialize;
|
||||
|
||||
mod errors;
|
||||
mod util;
|
||||
mod bundle;
|
||||
mod index;
|
||||
pub mod util;
|
||||
pub mod bundle;
|
||||
pub mod index;
|
||||
mod chunker;
|
||||
mod repository;
|
||||
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::io::Read;
|
||||
use std::time;
|
||||
|
||||
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 = "
|
||||
Usage:
|
||||
zvault init <repo>
|
||||
zvault info <repo>
|
||||
zvault bundles <repo>
|
||||
zvault init [--bundle-size SIZE] [--chunker METHOD] [--chunk-size SIZE] [--compression COMPRESSION] <repo>
|
||||
zvault backup [--full] <backup> <path>
|
||||
zvault restore <backup> <path>
|
||||
zvault check [--full] <repo>
|
||||
zvault list <repo>
|
||||
zvault info <backup>
|
||||
zvault stats <repo>
|
||||
zvault bundles <repo>
|
||||
zvault algotest <path>
|
||||
zvault test <repo> <path>
|
||||
zvault stat <path>
|
||||
zvault put <repo> <path>
|
||||
zvault put <backup> <path>
|
||||
|
||||
Options:
|
||||
--full Whether to verify the repository by loading all bundles
|
||||
--bundle-size SIZE The target size of a full bundle in MiB [default: 25]
|
||||
--chunker METHOD The chunking algorithm to use [default: fastcdc]
|
||||
--chunk-size SIZE The target average chunk size in KiB [default: 8]
|
||||
--compression COMPRESSION The compression to use [default: brotli/3]
|
||||
--full Whether to verify the repository by loading all bundles
|
||||
--bundle-size SIZE The target size of a full bundle in MiB [default: 25]
|
||||
--chunker METHOD The chunking algorithm to use [default: fastcdc]
|
||||
--chunk-size SIZE The target average chunk size in KiB [default: 8]
|
||||
--compression COMPRESSION The compression to use [default: brotli/3]
|
||||
";
|
||||
|
||||
|
||||
#[derive(RustcDecodable, Debug)]
|
||||
struct Args {
|
||||
cmd_init: bool,
|
||||
cmd_backup: bool,
|
||||
cmd_restore: bool,
|
||||
cmd_check: bool,
|
||||
|
||||
cmd_list: bool,
|
||||
cmd_info: bool,
|
||||
|
||||
cmd_stats: bool,
|
||||
cmd_bundles: bool,
|
||||
|
||||
cmd_algotest: bool,
|
||||
cmd_test: bool,
|
||||
cmd_stat: bool,
|
||||
cmd_check: bool,
|
||||
cmd_bundles: bool,
|
||||
cmd_put: bool,
|
||||
|
||||
arg_repo: Option<String>,
|
||||
arg_path: Option<String>,
|
||||
arg_backup: Option<String>,
|
||||
|
||||
flag_full: bool,
|
||||
flag_bundle_size: usize,
|
||||
flag_chunker: String,
|
||||
|
@ -100,14 +114,23 @@ fn main() {
|
|||
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 {
|
||||
repo.check(args.flag_full).unwrap();
|
||||
return
|
||||
}
|
||||
|
||||
if args.cmd_info {
|
||||
if args.cmd_stats {
|
||||
let info = repo.info();
|
||||
println!("Bundles: {}", info.bundle_count);
|
||||
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!("Chunk count: {}", info.chunk_count);
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -136,12 +168,6 @@ fn main() {
|
|||
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 {
|
||||
print!("Integrity check before...");
|
||||
repo.check(true).unwrap();
|
||||
|
@ -175,5 +201,29 @@ fn main() {
|
|||
let read_speed = data.len() as f64 / duration;
|
||||
assert_eq!(data.len(), data2.len());
|
||||
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 compression_ratio: f32,
|
||||
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,
|
||||
raw_data_size: raw_data_size,
|
||||
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::path::Path;
|
||||
use std::fs::{self, Metadata, File};
|
||||
use std::fs::{self, Metadata, File, Permissions};
|
||||
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};
|
||||
|
||||
|
||||
|
@ -118,25 +118,29 @@ impl Inode {
|
|||
}
|
||||
|
||||
#[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 mut file = None;
|
||||
match self.file_type {
|
||||
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 => {
|
||||
try!(fs::create_dir(&full_path).map_err(|_| "Failed to create directory"));
|
||||
},
|
||||
FileType::Symlink => {
|
||||
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 {
|
||||
return Err("Symlink without destination")
|
||||
}
|
||||
}
|
||||
}
|
||||
//FIXME: set times and permissions
|
||||
Ok(())
|
||||
try!(fs::set_permissions(&full_path, Permissions::from_mode(self.mode)).map_err(|_| "Failed to set permissions"));
|
||||
//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 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)
|
||||
}
|
||||
|
@ -169,4 +173,21 @@ impl Repository {
|
|||
let mut reader = rmp_serde::Deserializer::new(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 info;
|
||||
mod metadata;
|
||||
mod backup;
|
||||
|
||||
use std::mem;
|
||||
use std::cmp::max;
|
||||
|
@ -17,6 +18,7 @@ use super::chunker::Chunker;
|
|||
pub use self::config::Config;
|
||||
pub use self::metadata::Inode;
|
||||
pub use self::basic_io::Chunk;
|
||||
pub use self::backup::Backup;
|
||||
use self::bundle_map::BundleMap;
|
||||
|
||||
|
||||
|
@ -53,6 +55,7 @@ impl Repository {
|
|||
try!(config.save(path.join("config.yaml")).map_err(|_| "Failed to save config"));
|
||||
let bundle_map = BundleMap::create();
|
||||
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{
|
||||
path: path,
|
||||
chunker: config.chunker.create(),
|
||||
|
|
|
@ -33,14 +33,14 @@ impl Hash {
|
|||
impl fmt::Display for Hash {
|
||||
#[inline]
|
||||
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 {
|
||||
#[inline]
|
||||
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) {
|
||||
let mut tags: Vec<u64> = self.items.values().map(|&(_, n)| n).collect();
|
||||
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);
|
||||
new.extend(self.items.drain().filter(|&(_,(_, n))| n>=bar));
|
||||
new.extend(self.items.drain().filter(|&(_,(_, n))| n>=min));
|
||||
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