mirror of https://github.com/dswd/zvault
Restore and list
This commit is contained in:
parent
ba68fc2fda
commit
9fc70ea0fb
15
src/main.rs
15
src/main.rs
|
@ -34,6 +34,7 @@ Usage:
|
||||||
zvault check [--full] <repo>
|
zvault check [--full] <repo>
|
||||||
zvault backups <repo>
|
zvault backups <repo>
|
||||||
zvault info <backup>
|
zvault info <backup>
|
||||||
|
zvault list <backup> <path>
|
||||||
zvault stats <repo>
|
zvault stats <repo>
|
||||||
zvault bundles <repo>
|
zvault bundles <repo>
|
||||||
zvault algotest <path>
|
zvault algotest <path>
|
||||||
|
@ -57,6 +58,7 @@ struct Args {
|
||||||
|
|
||||||
cmd_backups: bool,
|
cmd_backups: bool,
|
||||||
cmd_info: bool,
|
cmd_info: bool,
|
||||||
|
cmd_list: bool,
|
||||||
|
|
||||||
cmd_stats: bool,
|
cmd_stats: bool,
|
||||||
cmd_bundles: bool,
|
cmd_bundles: bool,
|
||||||
|
@ -187,5 +189,18 @@ fn main() {
|
||||||
|
|
||||||
if args.cmd_restore {
|
if args.cmd_restore {
|
||||||
repo.restore_backup(&backup, &args.arg_path.unwrap()).unwrap();
|
repo.restore_backup(&backup, &args.arg_path.unwrap()).unwrap();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.cmd_list {
|
||||||
|
let inode = repo.get_backup_inode(&backup, &args.arg_path.unwrap()).unwrap();
|
||||||
|
println!("{}", inode.format_one_line());
|
||||||
|
if let Some(children) = inode.children {
|
||||||
|
for chunks in children.values() {
|
||||||
|
let inode = repo.get_inode(&chunks).unwrap();
|
||||||
|
println!("- {}", inode.format_one_line());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use super::{Repository, Chunk, RepositoryError};
|
use super::{Repository, Chunk, RepositoryError};
|
||||||
use super::metadata::FileType;
|
use super::metadata::{FileType, Inode};
|
||||||
|
|
||||||
use ::util::*;
|
use ::util::*;
|
||||||
|
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::path::Path;
|
use std::path::{self, Path};
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Backup {
|
pub struct Backup {
|
||||||
pub root: Vec<Chunk>,
|
pub root: Vec<Chunk>,
|
||||||
pub total_data_size: u64, // Sum of all raw sizes of all entities
|
pub total_data_size: u64, // Sum of all raw sizes of all entities
|
||||||
|
@ -73,9 +73,18 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore_backup<P: AsRef<Path>>(&mut self, backup: &Backup, path: P) -> Result<(), RepositoryError> {
|
pub fn restore_backup<P: AsRef<Path>>(&mut self, backup: &Backup, path: P) -> Result<(), RepositoryError> {
|
||||||
let inode = try!(self.get_inode(&backup.root));
|
let mut queue = VecDeque::new();
|
||||||
try!(self.save_inode_at(&inode, path));
|
queue.push_back((path.as_ref().to_owned(), try!(self.get_inode(&backup.root))));
|
||||||
//FIXME: recurse
|
while let Some((path, inode)) = queue.pop_front() {
|
||||||
|
try!(self.save_inode_at(&inode, &path));
|
||||||
|
if inode.file_type == FileType::Directory {
|
||||||
|
let path = path.join(inode.name);
|
||||||
|
for chunks in inode.children.unwrap().values() {
|
||||||
|
let inode = try!(self.get_inode(&chunks));
|
||||||
|
queue.push_back((path.clone(), inode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,4 +156,19 @@ impl Repository {
|
||||||
backup.avg_chunk_size = backup.deduplicated_data_size as f32 / backup.chunk_count as f32;
|
backup.avg_chunk_size = backup.deduplicated_data_size as f32 / backup.chunk_count as f32;
|
||||||
Ok(backup)
|
Ok(backup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_backup_inode<P: AsRef<Path>>(&mut self, backup: &Backup, path: P) -> Result<Inode, RepositoryError> {
|
||||||
|
let mut inode = try!(self.get_inode(&backup.root));
|
||||||
|
for c in path.as_ref().components() {
|
||||||
|
if let path::Component::Normal(name) = c {
|
||||||
|
let name = name.to_string_lossy();
|
||||||
|
if let Some(chunks) = inode.children.as_mut().and_then(|c| c.remove(&name as &str)) {
|
||||||
|
inode = try!(self.get_inode(&chunks));
|
||||||
|
} else {
|
||||||
|
return Err(RepositoryError::NoSuchFileInBackup(backup.clone(), path.as_ref().to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(inode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use super::backup::Backup;
|
||||||
use super::bundle_map::BundleMapError;
|
use super::bundle_map::BundleMapError;
|
||||||
use super::config::ConfigError;
|
use super::config::ConfigError;
|
||||||
use super::integrity::RepositoryIntegrityError;
|
use super::integrity::RepositoryIntegrityError;
|
||||||
|
@ -62,5 +63,9 @@ quick_error!{
|
||||||
description("Invalid file type")
|
description("Invalid file type")
|
||||||
display("{:?} has an invalid file type", path)
|
display("{:?} has an invalid file type", path)
|
||||||
}
|
}
|
||||||
|
NoSuchFileInBackup(backup: Backup, path: PathBuf) {
|
||||||
|
description("No such file in backup")
|
||||||
|
display("The backup does not contain the file {:?}", path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,14 @@ impl Inode {
|
||||||
// https://crates.io/crates/filetime
|
// https://crates.io/crates/filetime
|
||||||
Ok(file)
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format_one_line(&self) -> String {
|
||||||
|
match self.file_type {
|
||||||
|
FileType::Directory => format!("{:25}\t{} entries", format!("{}/", self.name), self.children.as_ref().unwrap().len()),
|
||||||
|
FileType::File => format!("{:25}\t{}", self.name, to_file_size(self.size)),
|
||||||
|
FileType::Symlink => format!("{:25}\t -> {}", self.name, self.symlink_target.as_ref().unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue