diff --git a/src/backups/integrity.rs b/src/backups/integrity.rs index 86b2501..72d5f6c 100644 --- a/src/backups/integrity.rs +++ b/src/backups/integrity.rs @@ -4,10 +4,17 @@ use super::*; use std::path::{Path, PathBuf}; +pub use ::repository::ModuleIntegrityReport; + quick_error!{ #[derive(Debug)] pub enum InodeIntegrityError { + BackupRead(path: PathBuf, err: Box) { + cause(err) + description(tr!("Backup unreadable")) + display("{}", tr_format!("Backup unreadable: {:?}\n\tcaused by: {}", path, err)) + } BrokenInode(path: PathBuf, err: Box) { cause(err) description(tr!("Broken inode")) @@ -27,32 +34,32 @@ pub trait RepositoryIntegrityIO { ) -> Result<(), RepositoryError>; fn check_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap, - lock: &OnlineMode - ) -> Result<(), RepositoryError>; + errors: &mut Vec, lock: &OnlineMode + ); + + fn check_backup_inode(&mut self, inode: &Inode, path: &Path, lock: &OnlineMode + ) -> ModuleIntegrityReport; + + fn check_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &OnlineMode + ) -> ModuleIntegrityReport; + + fn check_backups(&mut self, lock: &OnlineMode) -> ModuleIntegrityReport; fn check_and_repair_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap, - lock: &BackupMode + errors: &mut Vec, lock: &BackupMode ) -> Result, RepositoryError>; fn evacuate_broken_backup(&self, name: &str, lock: &BackupMode) -> Result<(), RepositoryError>; - fn check_backup_inode(&mut self, name: &str, backup: &mut BackupFile, path: &Path, - lock: &OnlineMode - ) -> Result<(), RepositoryError>; - fn check_and_repair_backup_inode(&mut self, name: &str, backup: &mut BackupFile, path: &Path, lock: &BackupMode, - ) -> Result<(), RepositoryError>; - - fn check_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &OnlineMode - ) -> Result<(), RepositoryError>; + ) -> Result, RepositoryError>; fn check_and_repair_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &BackupMode - ) -> Result<(), RepositoryError>; + ) -> Result, RepositoryError>; - fn check_backups(&mut self, lock: &OnlineMode) -> Result<(), RepositoryError>; - - fn check_and_repair_backups(&mut self, lock: &BackupMode) -> Result<(), RepositoryError>; + fn check_and_repair_backups(&mut self, lock: &BackupMode + ) -> Result, RepositoryError>; } @@ -78,31 +85,92 @@ impl RepositoryIntegrityIO for Repository { } fn check_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap, - lock: &OnlineMode, - ) -> Result<(), RepositoryError> { + errors: &mut Vec, lock: &OnlineMode + ) { let mut modified = false; match self.mark_chunks(checked, chunks, false) { - Ok(false) => return Ok(()), + Ok(false) => return, Ok(true) => (), - Err(err) => return Err(InodeIntegrityError::BrokenInode(path, Box::new(err)).into()), + Err(err) => { + errors.push(InodeIntegrityError::BrokenInode(path, Box::new(err))); + return + }, } - let mut inode = try!(self.get_inode(chunks, lock)); + let mut inode = match self.get_inode(chunks, lock) { + Ok(inode) => inode, + Err(err) => { + errors.push(InodeIntegrityError::BrokenInode(path, Box::new(err))); + return + } + }; // Mark the content chunks as used if let Err(err) = self.check_inode_contents(&inode, checked, lock) { - return Err(InodeIntegrityError::MissingInodeData(path, Box::new(err)).into()); + errors.push(InodeIntegrityError::MissingInodeData(path, Box::new(err))); + return } - // Put children in to do if let Some(ref mut children) = inode.children { for (name, chunks) in children.iter_mut() { - try!(self.check_subtree(path.join(name), chunks, checked, lock)); + self.check_subtree(path.join(name), chunks, checked, errors, lock); } } - try!(self.mark_chunks(checked, chunks, true)); - Ok(()) + self.mark_chunks(checked, chunks, true).unwrap(); } + fn check_backup_inode(&mut self, inode: &Inode, path: &Path, lock: &OnlineMode + ) -> ModuleIntegrityReport { + tr_info!("Checking inode..."); + let mut report = ModuleIntegrityReport { errors_unfixed: vec![], errors_fixed: vec![] }; + let mut checked = self.get_chunk_marker(); + if let Err(err) = self.check_inode_contents(inode, &mut checked, lock) { + report.errors_unfixed.push(InodeIntegrityError::MissingInodeData(path.to_path_buf(), Box::new(err))); + } + if let Some(ref children) = inode.children { + for (name, chunks) in children.iter() { + self.check_subtree(path.join(name), chunks, &mut checked, &mut report.errors_unfixed, lock); + } + } + report + } + + #[inline] + fn check_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &OnlineMode, + ) -> ModuleIntegrityReport { + tr_info!("Checking backup..."); + let mut checked = self.get_chunk_marker(); + let mut report = ModuleIntegrityReport { errors_unfixed: vec![], errors_fixed: vec![] }; + self.check_subtree(Path::new("").to_path_buf(), &backup.root, &mut checked, &mut report.errors_unfixed, lock); + report + } + + fn check_backups(&mut self, lock: &OnlineMode) -> ModuleIntegrityReport { + tr_info!("Checking backups..."); + let mut checked = self.get_chunk_marker(); + let mut report = ModuleIntegrityReport { errors_unfixed: vec![], errors_fixed: vec![] }; + let backup_map = match self.get_all_backups() { + Ok(backup_map) => backup_map, + Err(RepositoryError::BackupFile(BackupFileError::PartialBackupsList(backup_map, + failed))) => { + tr_warn!("Some backups could not be read, ignoring them"); + for path in &failed { + report.errors_unfixed.push(InodeIntegrityError::BackupRead(path.to_path_buf(), + Box::new(RepositoryError::BackupFile(BackupFileError::PartialBackupsList(backup_map.clone(), failed.clone()))) + )) + } + backup_map + }, + _ => return report + }; + for (name, mut backup) in ProgressIter::new(tr!("checking backups"), backup_map.len(), backup_map.into_iter()) { + let path = format!("{}::", name); + self.check_subtree(Path::new(&path).to_path_buf(), &backup.root, + &mut checked, &mut report.errors_unfixed, lock); + } + report + } + + fn check_and_repair_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap, - lock: &BackupMode, + errors: &mut Vec, lock: &BackupMode, ) -> Result, RepositoryError> { let mut modified = false; match self.mark_chunks(checked, chunks, false) { @@ -113,12 +181,7 @@ impl RepositoryIntegrityIO for Repository { let mut inode = try!(self.get_inode(chunks, lock.as_online())); // Mark the content chunks as used if let Err(err) = self.check_inode_contents(&inode, checked, lock.as_online()) { - tr_warn!( - "Problem detected: data of {:?} is corrupt\n\tcaused by: {}", - path, - err - ); - tr_info!("Removing inode data"); + errors.push(InodeIntegrityError::MissingInodeData(path.clone(), Box::new(err))); inode.data = Some(FileData::Inline(vec![].into())); inode.size = 0; modified = true; @@ -127,19 +190,14 @@ impl RepositoryIntegrityIO for Repository { if let Some(ref mut children) = inode.children { let mut removed = vec![]; for (name, chunks) in children.iter_mut() { - match self.check_and_repair_subtree(path.join(name), chunks, checked, lock) { + match self.check_and_repair_subtree(path.join(name), chunks, checked, errors, lock) { Ok(None) => (), Ok(Some(c)) => { *chunks = c; modified = true; } Err(err) => { - tr_warn!( - "Problem detected: inode {:?} is corrupt\n\tcaused by: {}", - path.join(name), - err - ); - tr_info!("Removing broken inode from backup"); + errors.push(InodeIntegrityError::BrokenInode(path.join(name), Box::new(err))); removed.push(name.to_string()); modified = true; } @@ -178,42 +236,17 @@ impl RepositoryIntegrityIO for Repository { Ok(()) } - fn check_backup_inode(&mut self, name: &str, backup: &mut BackupFile, path: &Path, - lock: &OnlineMode - ) -> Result<(), RepositoryError> { - tr_info!("Checking inode..."); - let mut checked = self.get_chunk_marker(); - let mut inodes = try!(self.get_backup_path(backup, path, lock)); - let mut inode = inodes.pop().unwrap(); - let mut modified = false; - if let Err(err) = self.check_inode_contents(&inode, &mut checked, lock) { - return Err( - InodeIntegrityError::MissingInodeData(path.to_path_buf(), Box::new(err)).into() - ); - } - if let Some(ref mut children) = inode.children { - for (name, chunks) in children.iter_mut() { - try!(self.check_subtree(path.join(name), chunks, &mut checked, lock)); - } - } - Ok(()) - } - fn check_and_repair_backup_inode(&mut self, name: &str, backup: &mut BackupFile, path: &Path, lock: &BackupMode - ) -> Result<(), RepositoryError> { + ) -> Result, RepositoryError> { tr_info!("Checking inode..."); let mut checked = self.get_chunk_marker(); let mut inodes = try!(self.get_backup_path(backup, path, lock.as_online())); let mut inode = inodes.pop().unwrap(); let mut modified = false; + let mut errors = vec![]; if let Err(err) = self.check_inode_contents(&inode, &mut checked, lock.as_online()) { - tr_warn!( - "Problem detected: data of {:?} is corrupt\n\tcaused by: {}", - path, - err - ); - tr_info!("Removing inode data"); + errors.push(InodeIntegrityError::MissingInodeData(path.to_path_buf(), Box::new(err))); inode.data = Some(FileData::Inline(vec![].into())); inode.size = 0; modified = true; @@ -221,19 +254,14 @@ impl RepositoryIntegrityIO for Repository { if let Some(ref mut children) = inode.children { let mut removed = vec![]; for (name, chunks) in children.iter_mut() { - match self.check_and_repair_subtree(path.join(name), chunks, &mut checked, lock) { + match self.check_and_repair_subtree(path.join(name), chunks, &mut checked, &mut errors, lock) { Ok(None) => (), Ok(Some(c)) => { *chunks = c; modified = true; } Err(err) => { - tr_warn!( - "Problem detected: inode {:?} is corrupt\n\tcaused by: {}", - path.join(name), - err - ); - tr_info!("Removing broken inode from backup"); + errors.push(InodeIntegrityError::BrokenInode(path.join(name), Box::new(err))); removed.push(name.to_string()); modified = true; } @@ -256,25 +284,17 @@ impl RepositoryIntegrityIO for Repository { try!(self.evacuate_broken_backup(name, lock)); try!(self.save_backup(backup, name, lock)); } - Ok(()) - } - - #[inline] - fn check_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &OnlineMode, - ) -> Result<(), RepositoryError> { - tr_info!("Checking backup..."); - let mut checked = self.get_chunk_marker(); - try!(self.check_subtree(Path::new("").to_path_buf(), &backup.root, &mut checked, lock)); - Ok(()) + Ok(ModuleIntegrityReport{errors_unfixed: vec![], errors_fixed: errors}) } #[inline] fn check_and_repair_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &BackupMode, - ) -> Result<(), RepositoryError> { + ) -> Result, RepositoryError> { tr_info!("Checking backup..."); let mut checked = self.get_chunk_marker(); + let mut errors = vec![]; match self.check_and_repair_subtree(Path::new("").to_path_buf(), - &backup.root, &mut checked, lock + &backup.root, &mut checked, &mut errors, lock ) { Ok(None) => (), Ok(Some(chunks)) => { @@ -285,43 +305,18 @@ impl RepositoryIntegrityIO for Repository { try!(self.save_backup(backup, name, lock)); } Err(err) => { - tr_warn!( - "The root of the backup {} has been corrupted\n\tcaused by: {}", - name, - err - ); + errors.push(InodeIntegrityError::BrokenInode(PathBuf::from("/"), Box::new(err))); try!(self.evacuate_broken_backup(name, lock)); } } - Ok(()) + Ok(ModuleIntegrityReport{errors_unfixed: vec![], errors_fixed: errors}) } - fn check_backups(&mut self, lock: &OnlineMode) -> Result<(), RepositoryError> { - tr_info!("Checking backups..."); - let mut checked = self.get_chunk_marker(); - let backup_map = match self.get_all_backups() { - Ok(backup_map) => backup_map, - Err(RepositoryError::BackupFile(BackupFileError::PartialBackupsList(backup_map, - _failed))) => { - tr_warn!("Some backups could not be read, ignoring them"); - backup_map - } - Err(err) => return Err(err), - }; - for (name, mut backup) in - ProgressIter::new(tr!("checking backups"), backup_map.len(), backup_map.into_iter()) - { - let path = format!("{}::", name); - try!(self.check_subtree(Path::new(&path).to_path_buf(), &backup.root, - &mut checked, lock)); - } - Ok(()) - } - - - fn check_and_repair_backups(&mut self, lock: &BackupMode) -> Result<(), RepositoryError> { + fn check_and_repair_backups(&mut self, lock: &BackupMode + ) -> Result, RepositoryError> { tr_info!("Checking backups..."); let mut checked = self.get_chunk_marker(); + let mut errors = vec![]; let backup_map = match self.get_all_backups() { Ok(backup_map) => backup_map, Err(RepositoryError::BackupFile(BackupFileError::PartialBackupsList(backup_map, @@ -339,6 +334,7 @@ impl RepositoryIntegrityIO for Repository { Path::new(&path).to_path_buf(), &backup.root, &mut checked, + &mut errors, lock ) { Ok(None) => (), @@ -350,15 +346,11 @@ impl RepositoryIntegrityIO for Repository { try!(self.save_backup(&backup, &name, lock)); } Err(err) => { - tr_warn!( - "The root of the backup {} has been corrupted\n\tcaused by: {}", - name, - err - ); + errors.push(InodeIntegrityError::BrokenInode(PathBuf::from(format!("{}::/", name)), Box::new(err))); try!(self.evacuate_broken_backup(&name, lock)); } } } - Ok(()) + Ok(ModuleIntegrityReport{errors_unfixed: vec![], errors_fixed: errors}) } } \ No newline at end of file diff --git a/src/backups/mod.rs b/src/backups/mod.rs index 37d8009..616adb6 100644 --- a/src/backups/mod.rs +++ b/src/backups/mod.rs @@ -321,7 +321,11 @@ impl BackupRepository { }) } - pub fn check(&mut self, options: CheckOptions) -> Result<(), RepositoryError> { + pub fn check(&mut self, options: &CheckOptions) -> Result<(), RepositoryError> { + self.0.online_mode(|r, l| { + r.check(options.index, options.bundles, options.bundle_data, l); + Ok(()) + }); unimplemented!() //TODO: implement } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 5eee4cd..6e19aed 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -827,7 +827,7 @@ pub fn run() -> Result<(), ErrorCode> { options.all_backups(); } checked!( - repo.check(options), + repo.check(&options), "check repository", ErrorCode::CheckRun ); diff --git a/src/repository/integrity.rs b/src/repository/integrity.rs index b7e700d..7550f30 100644 --- a/src/repository/integrity.rs +++ b/src/repository/integrity.rs @@ -46,15 +46,15 @@ quick_error!{ } -pub struct ModuleIntegrityReport { - pub errors_fixed: Vec, - pub errors_unfixed: Vec +pub struct ModuleIntegrityReport { + pub errors_fixed: Vec, + pub errors_unfixed: Vec } pub struct IntegrityReport { - pub bundle_map: Option, - pub index: Option, - pub bundles: Option + pub bundle_map: Option>, + pub index: Option>, + pub bundles: Option> } @@ -83,7 +83,7 @@ impl Repository { Ok(new) } - pub fn check_bundle_map(&mut self) -> ModuleIntegrityReport { + pub fn check_bundle_map(&mut self) -> ModuleIntegrityReport { tr_info!("Checking bundle map..."); let mut errors = vec![]; for (_id, bundle_id) in self.bundle_map.bundles() { @@ -120,7 +120,7 @@ impl Repository { self.save_bundle_map(lock.as_localwrite()) } - pub fn check_and_repair_bundle_map(&mut self, lock: &OnlineMode) -> Result { + pub fn check_and_repair_bundle_map(&mut self, lock: &OnlineMode) -> Result, RepositoryError> { let mut report = self.check_bundle_map(); if !report.errors_unfixed.is_empty() { try!(self.rebuild_bundle_map(lock)); @@ -183,7 +183,7 @@ impl Repository { } #[inline] - pub fn check_index(&mut self, lock: &ReadonlyMode) -> ModuleIntegrityReport { + pub fn check_index(&mut self, lock: &ReadonlyMode) -> ModuleIntegrityReport { tr_info!("Checking index integrity..."); let mut errors: Vec = self.index.check().into_iter().map(IntegrityError::Index).collect(); tr_info!("Checking index entries..."); @@ -191,7 +191,7 @@ impl Repository { ModuleIntegrityReport { errors_fixed: vec![], errors_unfixed: errors } } - pub fn check_and_repair_index(&mut self, lock: &OnlineMode) -> Result { + pub fn check_and_repair_index(&mut self, lock: &OnlineMode) -> Result, RepositoryError> { let mut report = self.check_index(lock.as_readonly()); if !report.errors_unfixed.is_empty() { try!(self.rebuild_index(lock)); @@ -201,7 +201,7 @@ impl Repository { } #[inline] - fn check_bundles_internal(&mut self, full: bool, lock: &OnlineMode) -> (ModuleIntegrityReport, Vec) { + fn check_bundles_internal(&mut self, full: bool, lock: &OnlineMode) -> (ModuleIntegrityReport, Vec) { tr_info!("Checking bundle integrity..."); let mut errors = vec![]; let mut bundles = vec![]; @@ -213,11 +213,11 @@ impl Repository { } #[inline] - pub fn check_bundles(&mut self, full: bool, lock: &OnlineMode) -> ModuleIntegrityReport { + pub fn check_bundles(&mut self, full: bool, lock: &OnlineMode) -> ModuleIntegrityReport { self.check_bundles_internal(full, lock).0 } - pub fn check_and_repair_bundles(&mut self, full: bool, lock: &VacuumMode) -> Result { + pub fn check_and_repair_bundles(&mut self, full: bool, lock: &VacuumMode) -> Result, RepositoryError> { let (mut report, bundles) = self.check_bundles_internal(full, lock.as_online()); if !report.errors_unfixed.is_empty() { try!(self.bundles.repair(lock, &bundles));