You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
6.1 KiB

5 years ago
use super::{Repository, Chunk, RepositoryError};
5 years ago
use super::metadata::FileType;
5 years ago
5 years ago
use ::util::*;
5 years ago
use std::fs::{self, File};
use std::path::Path;
5 years ago
use std::collections::HashMap;
use chrono::prelude::*;
5 years ago
#[derive(Default, Debug)]
pub struct Backup {
pub root: Vec<Chunk>,
5 years ago
pub total_data_size: u64, // Sum of all raw sizes of all entities
pub changed_data_size: u64, // Sum of all raw sizes of all entities actively stored
pub deduplicated_data_size: u64, // Sum of all raw sizes of all new bundles
pub encoded_data_size: u64, // Sum al all encoded sizes of all new bundles
pub bundle_count: usize,
5 years ago
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,
5 years ago
deduplicated_data_size: u64 => 3,
5 years ago
encoded_data_size: u64 => 4,
5 years ago
bundle_count: usize => 5,
5 years ago
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 {
5 years ago
pub fn list_backups(&self) -> Result<Vec<String>, RepositoryError> {
5 years ago
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() {
5 years ago
for entry in try!(fs::read_dir(path)) {
let entry = try!(entry);
5 years ago
let path = entry.path();
if path.is_dir() {
paths.push(path);
} else {
5 years ago
let relpath = path.strip_prefix(&base_path).unwrap();
5 years ago
backups.push(relpath.to_string_lossy().to_string());
}
}
}
Ok(backups)
}
5 years ago
pub fn get_backup(&self, name: &str) -> Result<Backup, RepositoryError> {
let mut file = try!(File::open(self.path.join("backups").join(name)));
Ok(try!(msgpack::decode_from_stream(&mut file)))
5 years ago
}
5 years ago
pub fn save_backup(&mut self, backup: &Backup, name: &str) -> Result<(), RepositoryError> {
let mut file = try!(File::create(self.path.join("backups").join(name)));
Ok(try!(msgpack::encode_to_stream(backup, &mut file)))
5 years ago
}
5 years ago
pub fn restore_backup<P: AsRef<Path>>(&mut self, backup: &Backup, path: P) -> Result<(), RepositoryError> {
5 years ago
let inode = try!(self.get_inode(&backup.root));
try!(self.save_inode_at(&inode, path));
5 years ago
//FIXME: recurse
5 years ago
Ok(())
}
5 years ago
#[allow(dead_code)]
5 years ago
pub fn create_full_backup<P: AsRef<Path>>(&mut self, path: P) -> Result<Backup, RepositoryError> {
5 years ago
let mut scan_stack = vec![path.as_ref().to_owned()];
let mut save_stack = vec![];
let mut directories = HashMap::new();
let mut backup = Backup::default();
let info_before = self.info();
let start = Local::now();
while let Some(path) = scan_stack.pop() {
// Create an inode for this path containing all attributes and contents
// (for files) but no children (for directories)
let mut inode = try!(self.create_inode(&path));
backup.total_data_size += inode.size;
backup.changed_data_size += inode.size;
if inode.file_type == FileType::Directory {
backup.dir_count +=1;
// For directories we need to put all children on the stack too, so there will be inodes created for them
// Also we put directories on the save stack to save them in order
save_stack.push(path.clone());
inode.children = Some(HashMap::new());
directories.insert(path.clone(), inode);
for ch in try!(fs::read_dir(&path)) {
scan_stack.push(try!(ch).path());
}
} else {
backup.file_count +=1;
// Non-directories are stored directly and the chunks are put into the children map of their parents
let chunks = try!(self.put_inode(&inode));
if let Some(parent) = path.parent() {
let parent = parent.to_owned();
if let Some(ref mut parent) = directories.get_mut(&parent) {
let children = parent.children.as_mut().unwrap();
children.insert(inode.name.clone(), chunks);
}
}
}
}
loop {
let path = save_stack.pop().unwrap();
// Now that all children have been saved the directories can be saved in order, adding their chunks to their parents as well
let inode = directories.remove(&path).unwrap();
let chunks = try!(self.put_inode(&inode));
if let Some(parent) = path.parent() {
let parent = parent.to_owned();
if let Some(ref mut parent) = directories.get_mut(&parent) {
let children = parent.children.as_mut().unwrap();
children.insert(inode.name.clone(), chunks);
} else if save_stack.is_empty() {
backup.root = chunks;
break
}
} else if save_stack.is_empty() {
backup.root = chunks;
break
}
}
try!(self.flush());
let elapsed = Local::now().signed_duration_since(start);
backup.date = start.timestamp();
backup.duration = elapsed.num_milliseconds() as f32 / 1_000.0;
let info_after = self.info();
backup.deduplicated_data_size = info_after.raw_data_size - info_before.raw_data_size;
backup.encoded_data_size = info_after.encoded_data_size - info_before.encoded_data_size;
backup.bundle_count = info_after.bundle_count - info_before.bundle_count;
backup.chunk_count = info_after.chunk_count - info_before.chunk_count;
backup.avg_chunk_size = backup.deduplicated_data_size as f32 / backup.chunk_count as f32;
Ok(backup)
}
5 years ago
}