This commit is contained in:
Dennis Schwerdel 2018-08-16 11:32:49 +02:00
parent 46e94bc7a6
commit 381bdf654d
4 changed files with 133 additions and 137 deletions

View File

@ -4,10 +4,17 @@ use super::*;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub use ::repository::ModuleIntegrityReport;
quick_error!{ quick_error!{
#[derive(Debug)] #[derive(Debug)]
pub enum InodeIntegrityError { pub enum InodeIntegrityError {
BackupRead(path: PathBuf, err: Box<RepositoryError>) {
cause(err)
description(tr!("Backup unreadable"))
display("{}", tr_format!("Backup unreadable: {:?}\n\tcaused by: {}", path, err))
}
BrokenInode(path: PathBuf, err: Box<RepositoryError>) { BrokenInode(path: PathBuf, err: Box<RepositoryError>) {
cause(err) cause(err)
description(tr!("Broken inode")) description(tr!("Broken inode"))
@ -27,32 +34,32 @@ pub trait RepositoryIntegrityIO {
) -> Result<(), RepositoryError>; ) -> Result<(), RepositoryError>;
fn check_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap, fn check_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap,
lock: &OnlineMode errors: &mut Vec<InodeIntegrityError>, lock: &OnlineMode
) -> Result<(), RepositoryError>; );
fn check_backup_inode(&mut self, inode: &Inode, path: &Path, lock: &OnlineMode
) -> ModuleIntegrityReport<InodeIntegrityError>;
fn check_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &OnlineMode
) -> ModuleIntegrityReport<InodeIntegrityError>;
fn check_backups(&mut self, lock: &OnlineMode) -> ModuleIntegrityReport<InodeIntegrityError>;
fn check_and_repair_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap, fn check_and_repair_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap,
lock: &BackupMode errors: &mut Vec<InodeIntegrityError>, lock: &BackupMode
) -> Result<Option<ChunkList>, RepositoryError>; ) -> Result<Option<ChunkList>, RepositoryError>;
fn evacuate_broken_backup(&self, name: &str, 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, fn check_and_repair_backup_inode(&mut self, name: &str, backup: &mut BackupFile, path: &Path,
lock: &BackupMode, lock: &BackupMode,
) -> Result<(), RepositoryError>; ) -> Result<ModuleIntegrityReport<InodeIntegrityError>, RepositoryError>;
fn check_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &OnlineMode
) -> Result<(), RepositoryError>;
fn check_and_repair_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &BackupMode fn check_and_repair_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &BackupMode
) -> Result<(), RepositoryError>; ) -> Result<ModuleIntegrityReport<InodeIntegrityError>, RepositoryError>;
fn check_backups(&mut self, lock: &OnlineMode) -> Result<(), RepositoryError>; fn check_and_repair_backups(&mut self, lock: &BackupMode
) -> Result<ModuleIntegrityReport<InodeIntegrityError>, 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, fn check_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap,
lock: &OnlineMode, errors: &mut Vec<InodeIntegrityError>, lock: &OnlineMode
) -> Result<(), RepositoryError> { ) {
let mut modified = false; let mut modified = false;
match self.mark_chunks(checked, chunks, false) { match self.mark_chunks(checked, chunks, false) {
Ok(false) => return Ok(()), Ok(false) => return,
Ok(true) => (), 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 // Mark the content chunks as used
if let Err(err) = self.check_inode_contents(&inode, checked, lock) { 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 { if let Some(ref mut children) = inode.children {
for (name, chunks) in children.iter_mut() { 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)); self.mark_chunks(checked, chunks, true).unwrap();
Ok(())
} }
fn check_backup_inode(&mut self, inode: &Inode, path: &Path, lock: &OnlineMode
) -> ModuleIntegrityReport<InodeIntegrityError> {
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<InodeIntegrityError> {
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<InodeIntegrityError> {
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, fn check_and_repair_subtree(&mut self, path: PathBuf, chunks: &[Chunk], checked: &mut Bitmap,
lock: &BackupMode, errors: &mut Vec<InodeIntegrityError>, lock: &BackupMode,
) -> Result<Option<ChunkList>, RepositoryError> { ) -> Result<Option<ChunkList>, RepositoryError> {
let mut modified = false; let mut modified = false;
match self.mark_chunks(checked, chunks, 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())); let mut inode = try!(self.get_inode(chunks, lock.as_online()));
// Mark the content chunks as used // Mark the content chunks as used
if let Err(err) = self.check_inode_contents(&inode, checked, lock.as_online()) { if let Err(err) = self.check_inode_contents(&inode, checked, lock.as_online()) {
tr_warn!( errors.push(InodeIntegrityError::MissingInodeData(path.clone(), Box::new(err)));
"Problem detected: data of {:?} is corrupt\n\tcaused by: {}",
path,
err
);
tr_info!("Removing inode data");
inode.data = Some(FileData::Inline(vec![].into())); inode.data = Some(FileData::Inline(vec![].into()));
inode.size = 0; inode.size = 0;
modified = true; modified = true;
@ -127,19 +190,14 @@ impl RepositoryIntegrityIO for Repository {
if let Some(ref mut children) = inode.children { if let Some(ref mut children) = inode.children {
let mut removed = vec![]; let mut removed = vec![];
for (name, chunks) in children.iter_mut() { 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(None) => (),
Ok(Some(c)) => { Ok(Some(c)) => {
*chunks = c; *chunks = c;
modified = true; modified = true;
} }
Err(err) => { Err(err) => {
tr_warn!( errors.push(InodeIntegrityError::BrokenInode(path.join(name), Box::new(err)));
"Problem detected: inode {:?} is corrupt\n\tcaused by: {}",
path.join(name),
err
);
tr_info!("Removing broken inode from backup");
removed.push(name.to_string()); removed.push(name.to_string());
modified = true; modified = true;
} }
@ -178,42 +236,17 @@ impl RepositoryIntegrityIO for Repository {
Ok(()) 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, fn check_and_repair_backup_inode(&mut self, name: &str, backup: &mut BackupFile, path: &Path,
lock: &BackupMode lock: &BackupMode
) -> Result<(), RepositoryError> { ) -> Result<ModuleIntegrityReport<InodeIntegrityError>, RepositoryError> {
tr_info!("Checking inode..."); tr_info!("Checking inode...");
let mut checked = self.get_chunk_marker(); let mut checked = self.get_chunk_marker();
let mut inodes = try!(self.get_backup_path(backup, path, lock.as_online())); let mut inodes = try!(self.get_backup_path(backup, path, lock.as_online()));
let mut inode = inodes.pop().unwrap(); let mut inode = inodes.pop().unwrap();
let mut modified = false; let mut modified = false;
let mut errors = vec![];
if let Err(err) = self.check_inode_contents(&inode, &mut checked, lock.as_online()) { if let Err(err) = self.check_inode_contents(&inode, &mut checked, lock.as_online()) {
tr_warn!( errors.push(InodeIntegrityError::MissingInodeData(path.to_path_buf(), Box::new(err)));
"Problem detected: data of {:?} is corrupt\n\tcaused by: {}",
path,
err
);
tr_info!("Removing inode data");
inode.data = Some(FileData::Inline(vec![].into())); inode.data = Some(FileData::Inline(vec![].into()));
inode.size = 0; inode.size = 0;
modified = true; modified = true;
@ -221,19 +254,14 @@ impl RepositoryIntegrityIO for Repository {
if let Some(ref mut children) = inode.children { if let Some(ref mut children) = inode.children {
let mut removed = vec![]; let mut removed = vec![];
for (name, chunks) in children.iter_mut() { 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(None) => (),
Ok(Some(c)) => { Ok(Some(c)) => {
*chunks = c; *chunks = c;
modified = true; modified = true;
} }
Err(err) => { Err(err) => {
tr_warn!( errors.push(InodeIntegrityError::BrokenInode(path.join(name), Box::new(err)));
"Problem detected: inode {:?} is corrupt\n\tcaused by: {}",
path.join(name),
err
);
tr_info!("Removing broken inode from backup");
removed.push(name.to_string()); removed.push(name.to_string());
modified = true; modified = true;
} }
@ -256,25 +284,17 @@ impl RepositoryIntegrityIO for Repository {
try!(self.evacuate_broken_backup(name, lock)); try!(self.evacuate_broken_backup(name, lock));
try!(self.save_backup(backup, name, lock)); try!(self.save_backup(backup, name, lock));
} }
Ok(()) Ok(ModuleIntegrityReport{errors_unfixed: vec![], errors_fixed: errors})
}
#[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(())
} }
#[inline] #[inline]
fn check_and_repair_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &BackupMode, fn check_and_repair_backup(&mut self, name: &str, backup: &mut BackupFile, lock: &BackupMode,
) -> Result<(), RepositoryError> { ) -> Result<ModuleIntegrityReport<InodeIntegrityError>, RepositoryError> {
tr_info!("Checking backup..."); tr_info!("Checking backup...");
let mut checked = self.get_chunk_marker(); let mut checked = self.get_chunk_marker();
let mut errors = vec![];
match self.check_and_repair_subtree(Path::new("").to_path_buf(), 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(None) => (),
Ok(Some(chunks)) => { Ok(Some(chunks)) => {
@ -285,43 +305,18 @@ impl RepositoryIntegrityIO for Repository {
try!(self.save_backup(backup, name, lock)); try!(self.save_backup(backup, name, lock));
} }
Err(err) => { Err(err) => {
tr_warn!( errors.push(InodeIntegrityError::BrokenInode(PathBuf::from("/"), Box::new(err)));
"The root of the backup {} has been corrupted\n\tcaused by: {}",
name,
err
);
try!(self.evacuate_broken_backup(name, lock)); 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> { fn check_and_repair_backups(&mut self, lock: &BackupMode
tr_info!("Checking backups..."); ) -> Result<ModuleIntegrityReport<InodeIntegrityError>, RepositoryError> {
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> {
tr_info!("Checking backups..."); tr_info!("Checking backups...");
let mut checked = self.get_chunk_marker(); let mut checked = self.get_chunk_marker();
let mut errors = vec![];
let backup_map = match self.get_all_backups() { let backup_map = match self.get_all_backups() {
Ok(backup_map) => backup_map, Ok(backup_map) => backup_map,
Err(RepositoryError::BackupFile(BackupFileError::PartialBackupsList(backup_map, Err(RepositoryError::BackupFile(BackupFileError::PartialBackupsList(backup_map,
@ -339,6 +334,7 @@ impl RepositoryIntegrityIO for Repository {
Path::new(&path).to_path_buf(), Path::new(&path).to_path_buf(),
&backup.root, &backup.root,
&mut checked, &mut checked,
&mut errors,
lock lock
) { ) {
Ok(None) => (), Ok(None) => (),
@ -350,15 +346,11 @@ impl RepositoryIntegrityIO for Repository {
try!(self.save_backup(&backup, &name, lock)); try!(self.save_backup(&backup, &name, lock));
} }
Err(err) => { Err(err) => {
tr_warn!( errors.push(InodeIntegrityError::BrokenInode(PathBuf::from(format!("{}::/", name)), Box::new(err)));
"The root of the backup {} has been corrupted\n\tcaused by: {}",
name,
err
);
try!(self.evacuate_broken_backup(&name, lock)); try!(self.evacuate_broken_backup(&name, lock));
} }
} }
} }
Ok(()) Ok(ModuleIntegrityReport{errors_unfixed: vec![], errors_fixed: errors})
} }
} }

View File

@ -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!() unimplemented!()
//TODO: implement //TODO: implement
} }

View File

@ -827,7 +827,7 @@ pub fn run() -> Result<(), ErrorCode> {
options.all_backups(); options.all_backups();
} }
checked!( checked!(
repo.check(options), repo.check(&options),
"check repository", "check repository",
ErrorCode::CheckRun ErrorCode::CheckRun
); );

View File

@ -46,15 +46,15 @@ quick_error!{
} }
pub struct ModuleIntegrityReport { pub struct ModuleIntegrityReport<T> {
pub errors_fixed: Vec<IntegrityError>, pub errors_fixed: Vec<T>,
pub errors_unfixed: Vec<IntegrityError> pub errors_unfixed: Vec<T>
} }
pub struct IntegrityReport { pub struct IntegrityReport {
pub bundle_map: Option<ModuleIntegrityReport>, pub bundle_map: Option<ModuleIntegrityReport<IntegrityError>>,
pub index: Option<ModuleIntegrityReport>, pub index: Option<ModuleIntegrityReport<IntegrityError>>,
pub bundles: Option<ModuleIntegrityReport> pub bundles: Option<ModuleIntegrityReport<IntegrityError>>
} }
@ -83,7 +83,7 @@ impl Repository {
Ok(new) Ok(new)
} }
pub fn check_bundle_map(&mut self) -> ModuleIntegrityReport { pub fn check_bundle_map(&mut self) -> ModuleIntegrityReport<IntegrityError> {
tr_info!("Checking bundle map..."); tr_info!("Checking bundle map...");
let mut errors = vec![]; let mut errors = vec![];
for (_id, bundle_id) in self.bundle_map.bundles() { for (_id, bundle_id) in self.bundle_map.bundles() {
@ -120,7 +120,7 @@ impl Repository {
self.save_bundle_map(lock.as_localwrite()) self.save_bundle_map(lock.as_localwrite())
} }
pub fn check_and_repair_bundle_map(&mut self, lock: &OnlineMode) -> Result<ModuleIntegrityReport, RepositoryError> { pub fn check_and_repair_bundle_map(&mut self, lock: &OnlineMode) -> Result<ModuleIntegrityReport<IntegrityError>, RepositoryError> {
let mut report = self.check_bundle_map(); let mut report = self.check_bundle_map();
if !report.errors_unfixed.is_empty() { if !report.errors_unfixed.is_empty() {
try!(self.rebuild_bundle_map(lock)); try!(self.rebuild_bundle_map(lock));
@ -183,7 +183,7 @@ impl Repository {
} }
#[inline] #[inline]
pub fn check_index(&mut self, lock: &ReadonlyMode) -> ModuleIntegrityReport { pub fn check_index(&mut self, lock: &ReadonlyMode) -> ModuleIntegrityReport<IntegrityError> {
tr_info!("Checking index integrity..."); tr_info!("Checking index integrity...");
let mut errors: Vec<IntegrityError> = self.index.check().into_iter().map(IntegrityError::Index).collect(); let mut errors: Vec<IntegrityError> = self.index.check().into_iter().map(IntegrityError::Index).collect();
tr_info!("Checking index entries..."); tr_info!("Checking index entries...");
@ -191,7 +191,7 @@ impl Repository {
ModuleIntegrityReport { errors_fixed: vec![], errors_unfixed: errors } ModuleIntegrityReport { errors_fixed: vec![], errors_unfixed: errors }
} }
pub fn check_and_repair_index(&mut self, lock: &OnlineMode) -> Result<ModuleIntegrityReport, RepositoryError> { pub fn check_and_repair_index(&mut self, lock: &OnlineMode) -> Result<ModuleIntegrityReport<IntegrityError>, RepositoryError> {
let mut report = self.check_index(lock.as_readonly()); let mut report = self.check_index(lock.as_readonly());
if !report.errors_unfixed.is_empty() { if !report.errors_unfixed.is_empty() {
try!(self.rebuild_index(lock)); try!(self.rebuild_index(lock));
@ -201,7 +201,7 @@ impl Repository {
} }
#[inline] #[inline]
fn check_bundles_internal(&mut self, full: bool, lock: &OnlineMode) -> (ModuleIntegrityReport, Vec<BundleId>) { fn check_bundles_internal(&mut self, full: bool, lock: &OnlineMode) -> (ModuleIntegrityReport<IntegrityError>, Vec<BundleId>) {
tr_info!("Checking bundle integrity..."); tr_info!("Checking bundle integrity...");
let mut errors = vec![]; let mut errors = vec![];
let mut bundles = vec![]; let mut bundles = vec![];
@ -213,11 +213,11 @@ impl Repository {
} }
#[inline] #[inline]
pub fn check_bundles(&mut self, full: bool, lock: &OnlineMode) -> ModuleIntegrityReport { pub fn check_bundles(&mut self, full: bool, lock: &OnlineMode) -> ModuleIntegrityReport<IntegrityError> {
self.check_bundles_internal(full, lock).0 self.check_bundles_internal(full, lock).0
} }
pub fn check_and_repair_bundles(&mut self, full: bool, lock: &VacuumMode) -> Result<ModuleIntegrityReport, RepositoryError> { pub fn check_and_repair_bundles(&mut self, full: bool, lock: &VacuumMode) -> Result<ModuleIntegrityReport<IntegrityError>, RepositoryError> {
let (mut report, bundles) = self.check_bundles_internal(full, lock.as_online()); let (mut report, bundles) = self.check_bundles_internal(full, lock.as_online());
if !report.errors_unfixed.is_empty() { if !report.errors_unfixed.is_empty() {
try!(self.bundles.repair(lock, &bundles)); try!(self.bundles.repair(lock, &bundles));