From 0d94a7561338a5f50ecceb4ccbd111dc46877917 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Thu, 23 Mar 2017 09:31:23 +0100 Subject: [PATCH] Code cleanup --- src/cli/mod.rs | 29 +++++++- src/index.rs | 10 ++- src/main.rs | 2 +- src/repository/backup.rs | 141 ++++++++++++------------------------ src/repository/integrity.rs | 20 ++--- src/repository/metadata.rs | 10 +++ 6 files changed, 96 insertions(+), 116 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index f0fcf2a..5401250 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -76,6 +76,26 @@ fn print_backup(backup: &Backup) { println!("Chunk count: {}, avg size: {}", backup.chunk_count, to_file_size(backup.avg_chunk_size as u64)); } +fn print_inode(inode: &Inode) { + println!("Name: {}", inode.name); + println!("Type: {}", inode.file_type); + println!("Size: {}", to_file_size(inode.size)); + println!("Permissions: {:3o}", inode.mode); + println!("User: {}", inode.user); + println!("Group: {}", inode.group); + println!("Access time: {}", Local.timestamp(inode.access_time, 0).to_rfc2822()); + println!("Modification time: {}", Local.timestamp(inode.modify_time, 0).to_rfc2822()); + if let Some(ref target) = inode.symlink_target { + println!("Symlink target: {}", target); + } + if let Some(ref children) = inode.children { + println!("Children:"); + for name in children.keys() { + println!(" - {}", name); + } + } +} + fn print_backups(backup_map: &HashMap) { for (name, backup) in backup_map { println!("{:25} {:>32} {:5} files, {:4} dirs, {:>10}", @@ -168,7 +188,7 @@ pub fn run() { info!("No reference backup found, doing a full scan instead"); } } - let backup = match repo.create_backup(&src_path, reference_backup.as_ref()) { + let backup = match repo.create_backup_recursively(&src_path, reference_backup.as_ref()) { Ok(backup) => backup, Err(RepositoryError::Backup(BackupError::FailedPaths(backup, _failed_paths))) => { warn!("Some files are missing form the backup"); @@ -265,11 +285,12 @@ pub fn run() { } }, Arguments::Info{repo_path, backup_name, inode} => { - let repo = open_repository(&repo_path); + let mut repo = open_repository(&repo_path); if let Some(backup_name) = backup_name { let backup = get_backup(&repo, &backup_name); - if let Some(_inode) = inode { - error!("Displaying information on single inodes is not implemented yet"); + if let Some(inode) = inode { + let inode = checked(repo.get_backup_inode(&backup, inode), "load subpath inode"); + print_inode(&inode); } else { print_backup(&backup); } diff --git a/src/index.rs b/src/index.rs index 4a5ce9e..2aa0bab 100644 --- a/src/index.rs +++ b/src/index.rs @@ -460,23 +460,25 @@ impl Index { } #[inline] - pub fn walk(&self, mut f: F) where F: FnMut(&Hash, &Location) { + pub fn walk(&self, mut f: F) -> Result<(), E> where F: FnMut(&Hash, &Location) -> Result<(), E> { for pos in 0..self.capacity { let entry = &self.data[pos]; if entry.is_used() { - f(&entry.key, &entry.data); + try!(f(&entry.key, &entry.data)); } } + Ok(()) } #[inline] - pub fn walk_mut(&mut self, mut f: F) where F: FnMut(&Hash, &mut Location) { + pub fn walk_mut(&mut self, mut f: F) -> Result<(), E> where F: FnMut(&Hash, &mut Location) -> Result<(), E> { for pos in 0..self.capacity { let entry = &mut self.data[pos]; if entry.is_used() { - f(&entry.key, &mut entry.data); + try!(f(&entry.key, &mut entry.data)); } } + Ok(()) } #[inline] diff --git a/src/main.rs b/src/main.rs index 507781d..f1a7dc0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ extern crate filetime; extern crate libc; pub mod util; -pub mod bundledb; +mod bundledb; pub mod index; mod chunker; mod repository; diff --git a/src/repository/backup.rs b/src/repository/backup.rs index 67af73a..af9dd39 100644 --- a/src/repository/backup.rs +++ b/src/repository/backup.rs @@ -307,12 +307,55 @@ impl Repository { self.restore_inode_tree(inode, path) } + + pub fn create_backup_recurse>( + &mut self, + path: P, + reference: Option<&Inode>, + backup: &mut Backup, + failed_paths: &mut Vec + ) -> Result { + let path = path.as_ref(); + let mut inode = try!(self.create_inode(path, reference)); + let meta_size = 1000; // add 1000 for encoded metadata + backup.total_data_size += inode.size + meta_size; + if let Some(ref_inode) = reference { + if !ref_inode.is_unchanged(&inode) { + backup.changed_data_size += inode.size + meta_size; + } + } else { + backup.changed_data_size += inode.size + meta_size; + } + if inode.file_type == FileType::Directory { + backup.dir_count +=1; + let mut children = BTreeMap::new(); + for ch in try!(fs::read_dir(path)) { + let child = try!(ch); + let name = child.file_name().to_string_lossy().to_string(); + let ref_child = reference.as_ref() + .and_then(|inode| inode.children.as_ref()) + .and_then(|map| map.get(&name)) + .and_then(|chunks| self.get_inode(chunks).ok()); + let child_path = child.path(); + let chunks = match self.create_backup_recurse(&child_path, ref_child.as_ref(), backup, failed_paths) { + Ok(chunks) => chunks, + Err(_) => { + failed_paths.push(child_path); + continue + } + }; + children.insert(name, chunks); + } + inode.children = Some(children); + } else { + backup.file_count +=1; + } + self.put_inode(&inode) + } + #[allow(dead_code)] - pub fn create_backup>(&mut self, path: P, reference: Option<&Backup>) -> Result { + pub fn create_backup_recursively>(&mut self, path: P, reference: Option<&Backup>) -> Result { let reference_inode = reference.and_then(|b| self.get_inode(&b.root).ok()); - let mut scan_stack = vec![(path.as_ref().to_owned(), reference_inode)]; - let mut save_stack = vec![]; - let mut directories = HashMap::new(); let mut backup = Backup::default(); backup.config = self.config.clone(); backup.host = get_hostname().unwrap_or_else(|_| "".to_string()); @@ -320,95 +363,7 @@ impl Repository { let info_before = self.info(); let start = Local::now(); let mut failed_paths = vec![]; - while let Some((path, reference_inode)) = scan_stack.pop() { - // Create an inode for this path containing all attributes and contents - // (for files) but no children (for directories) - let mut inode = match self.create_inode(&path, reference_inode.as_ref()) { - Ok(inode) => inode, - Err(RepositoryError::Inode(err)) => { - warn!("Failed to backup inode {}", err); - failed_paths.push(path); - continue - }, - Err(err) => return Err(err) - }; - let meta_size = 1000; // add 1000 for encoded metadata - backup.total_data_size += inode.size + meta_size; - if let Some(ref ref_inode) = reference_inode { - if !ref_inode.is_unchanged(&inode) { - backup.changed_data_size += inode.size + meta_size; - } - } else { - backup.changed_data_size += inode.size + meta_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(BTreeMap::new()); - directories.insert(path.clone(), inode); - let dirlist = match fs::read_dir(&path) { - Ok(dirlist) => dirlist, - Err(err) => { - warn!("Failed to read {:?}: {}", &path, err); - failed_paths.push(path); - continue - } - }; - for ch in dirlist { - let child = match ch { - Ok(child) => child, - Err(err) => { - warn!("Failed to read {:?}: {}", &path, err); - continue - } - }; - let name = child.file_name().to_string_lossy().to_string(); - let ref_child = reference_inode.as_ref() - .and_then(|inode| inode.children.as_ref()) - .and_then(|map| map.get(&name)) - .and_then(|chunks| self.get_inode(chunks).ok()); - scan_stack.push((child.path(), ref_child)); - } - } else { - backup.file_count +=1; - // Non-directories are stored directly and the chunks are put into the children map of their parents - if let Some(parent) = path.parent() { - let parent = parent.to_owned(); - if !directories.contains_key(&parent) { - // This is a backup of one one file, put it in the directories map so it will be saved later - assert!(scan_stack.is_empty() && save_stack.is_empty() && directories.is_empty()); - save_stack.push(path.clone()); - directories.insert(path.clone(), inode); - } else { - let mut parent = directories.get_mut(&parent).unwrap(); - let chunks = try!(self.put_inode(&inode)); - 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 - } - } + backup.root = try!(self.create_backup_recurse(path, reference_inode.as_ref(), &mut backup, &mut failed_paths)); try!(self.flush()); let elapsed = Local::now().signed_duration_since(start); backup.date = start.timestamp(); diff --git a/src/repository/integrity.rs b/src/repository/integrity.rs index 3b7187d..6bb250e 100644 --- a/src/repository/integrity.rs +++ b/src/repository/integrity.rs @@ -32,16 +32,9 @@ quick_error!{ impl Repository { fn check_index_chunks(&self) -> Result<(), RepositoryError> { - let mut pos = 0; - loop { - pos = if let Some(pos) = self.index.next_entry(pos) { - pos - } else { - break - }; - let entry = self.index.get_entry(pos).unwrap(); + self.index.walk(|_hash, location| { // Lookup bundle id from map - let bundle_id = try!(self.get_bundle_id(entry.data.bundle)); + let bundle_id = try!(self.get_bundle_id(location.bundle)); // Get bundle object from bundledb let bundle = if let Some(bundle) = self.bundles.get_bundle_info(&bundle_id) { bundle @@ -49,12 +42,11 @@ impl Repository { return Err(RepositoryIntegrityError::MissingBundle(bundle_id.clone()).into()) }; // Get chunk from bundle - if bundle.chunk_count <= entry.data.chunk as usize { - return Err(RepositoryIntegrityError::NoSuchChunk(bundle_id.clone(), entry.data.chunk).into()) + if bundle.chunk_count <= location.chunk as usize { + return Err(RepositoryIntegrityError::NoSuchChunk(bundle_id.clone(), location.chunk).into()) } - pos += 1; - } - Ok(()) + Ok(()) + }) } fn check_repository(&self) -> Result<(), RepositoryError> { diff --git a/src/repository/metadata.rs b/src/repository/metadata.rs index 357c521..6e52a64 100644 --- a/src/repository/metadata.rs +++ b/src/repository/metadata.rs @@ -8,6 +8,7 @@ use std::fs::{self, File, Permissions}; use std::os::linux::fs::MetadataExt; use std::os::unix::fs::{PermissionsExt, symlink}; use std::io::{self, Read, Write}; +use std::fmt; quick_error!{ @@ -78,6 +79,15 @@ serde_impl!(FileType(u8) { Directory => 1, Symlink => 2 }); +impl fmt::Display for FileType { + fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + FileType::File => write!(format, "file"), + FileType::Directory => write!(format, "directory"), + FileType::Symlink => write!(format, "symlink") + } + } +} #[derive(Debug, Clone)]