zvault/src/repository/integrity.rs

219 lines
7.4 KiB
Rust
Raw Normal View History

2017-07-21 09:21:59 +00:00
use prelude::*;
2017-03-10 11:43:32 +00:00
2017-04-12 18:19:21 +00:00
use super::*;
2017-04-11 06:49:45 +00:00
use std::time::Duration;
use pbr::ProgressBar;
2017-03-10 11:43:32 +00:00
2017-03-16 08:42:30 +00:00
quick_error!{
#[derive(Debug)]
pub enum IntegrityError {
2017-03-16 08:42:30 +00:00
MissingChunk(hash: Hash) {
2018-02-24 12:19:51 +00:00
description(tr!("Missing chunk"))
display("{}", tr_format!("Missing chunk: {}", hash))
2017-03-16 08:42:30 +00:00
}
MissingBundleId(id: u32) {
2018-02-24 12:19:51 +00:00
description(tr!("Missing bundle"))
display("{}", tr_format!("Missing bundle: {}", id))
2017-03-16 08:42:30 +00:00
}
MissingBundle(id: BundleId) {
2018-02-24 12:19:51 +00:00
description(tr!("Missing bundle"))
display("{}", tr_format!("Missing bundle: {}", id))
2017-03-16 08:42:30 +00:00
}
NoSuchChunk(bundle: BundleId, chunk: u32) {
2018-02-24 12:19:51 +00:00
description(tr!("No such chunk"))
display("{}", tr_format!("Bundle {} does not contain the chunk {}", bundle, chunk))
2017-03-16 08:42:30 +00:00
}
2017-04-10 16:21:26 +00:00
RemoteBundlesNotInMap {
2018-02-24 12:19:51 +00:00
description(tr!("Remote bundles missing from map"))
2017-04-10 16:21:26 +00:00
}
MapContainsDuplicates {
2018-02-24 12:19:51 +00:00
description(tr!("Map contains duplicates"))
2017-04-10 16:21:26 +00:00
}
2017-03-16 08:42:30 +00:00
}
}
2018-03-10 15:35:40 +00:00
2017-03-10 11:43:32 +00:00
impl Repository {
2018-03-10 15:35:40 +00:00
pub fn get_chunk_marker(&self) -> Bitmap {
Bitmap::new(self.index.capacity())
2017-03-20 13:03:29 +00:00
}
2018-03-10 15:35:40 +00:00
pub fn check_chunks(
2017-07-21 09:21:59 +00:00
&self,
checked: &mut Bitmap,
chunks: &[Chunk],
mark: bool,
) -> Result<bool, RepositoryError> {
2017-03-20 13:03:29 +00:00
let mut new = false;
for &(hash, _len) in chunks {
if let Some(pos) = self.index.pos(&hash) {
2017-03-20 21:24:53 +00:00
new |= !checked.get(pos);
if mark {
checked.set(pos);
}
2017-03-20 13:03:29 +00:00
} else {
2017-07-21 09:21:59 +00:00
return Err(IntegrityError::MissingChunk(hash).into());
2017-03-20 13:03:29 +00:00
}
}
Ok(new)
}
2018-03-10 15:35:40 +00:00
fn check_index_chunks(&self) -> Result<(), RepositoryError> {
let mut progress = ProgressBar::new(self.index.len() as u64);
progress.message(tr!("checking index: "));
progress.set_max_refresh_rate(Some(Duration::from_millis(100)));
for (count, (_hash, location)) in self.index.iter().enumerate() {
// Lookup bundle id from map
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
2017-04-13 05:41:18 +00:00
} else {
2018-03-10 15:35:40 +00:00
progress.finish_print(tr!("checking index: done."));
return Err(IntegrityError::MissingBundle(bundle_id.clone()).into());
};
// Get chunk from bundle
if bundle.info.chunk_count <= location.chunk as usize {
progress.finish_print(tr!("checking index: done."));
2017-07-21 09:21:59 +00:00
return Err(
2018-03-10 15:35:40 +00:00
IntegrityError::NoSuchChunk(bundle_id.clone(), location.chunk).into()
2017-07-21 09:21:59 +00:00
);
2017-03-22 10:10:13 +00:00
}
2018-03-10 15:35:40 +00:00
if count % 1000 == 0 {
progress.set(count as u64);
2017-04-13 05:46:16 +00:00
}
}
2018-03-10 15:35:40 +00:00
progress.finish_print(tr!("checking index: done."));
2017-04-10 16:21:26 +00:00
Ok(())
}
2017-04-12 18:19:21 +00:00
pub fn rebuild_bundle_map(&mut self) -> Result<(), RepositoryError> {
2018-02-24 12:19:51 +00:00
tr_info!("Rebuilding bundle map from bundles");
2017-04-12 18:19:21 +00:00
self.bundle_map = BundleMap::create();
for bundle in self.bundles.list_bundles() {
let bundle_id = match bundle.mode {
BundleMode::Data => self.next_data_bundle,
2017-07-21 09:21:59 +00:00
BundleMode::Meta => self.next_meta_bundle,
2017-04-12 18:19:21 +00:00
};
self.bundle_map.set(bundle_id, bundle.id.clone());
if self.next_meta_bundle == bundle_id {
self.next_meta_bundle = self.next_free_bundle_id()
}
if self.next_data_bundle == bundle_id {
self.next_data_bundle = self.next_free_bundle_id()
}
}
self.save_bundle_map()
}
pub fn rebuild_index(&mut self) -> Result<(), RepositoryError> {
2018-02-24 12:19:51 +00:00
tr_info!("Rebuilding index from bundles");
2017-04-12 18:19:21 +00:00
self.index.clear();
let mut bundles = self.bundle_map.bundles();
bundles.sort_by_key(|&(_, ref v)| v.clone());
2018-02-24 12:19:51 +00:00
for (num, id) in ProgressIter::new(tr!("Rebuilding index from bundles"), bundles.len(), bundles.into_iter()) {
2017-04-12 18:19:21 +00:00
let chunks = try!(self.bundles.get_chunk_list(&id));
for (i, (hash, _len)) in chunks.into_inner().into_iter().enumerate() {
2017-07-21 09:21:59 +00:00
try!(self.index.set(
&hash,
&Location {
bundle: num as u32,
chunk: i as u32
}
));
2017-04-12 18:19:21 +00:00
}
}
Ok(())
}
2017-04-10 18:35:28 +00:00
#[inline]
2017-04-12 18:19:21 +00:00
pub fn check_index(&mut self, repair: bool) -> Result<(), RepositoryError> {
if repair {
try!(self.write_mode());
}
2018-02-24 12:19:51 +00:00
tr_info!("Checking index integrity...");
2017-04-12 18:19:21 +00:00
if let Err(err) = self.index.check() {
if repair {
2018-02-24 12:19:51 +00:00
tr_warn!(
2017-07-21 09:21:59 +00:00
"Problem detected: index was corrupted\n\tcaused by: {}",
err
);
2017-04-12 18:19:21 +00:00
return self.rebuild_index();
} else {
2017-07-21 09:21:59 +00:00
return Err(err.into());
2017-04-12 18:19:21 +00:00
}
}
2018-02-24 12:19:51 +00:00
tr_info!("Checking index entries...");
2017-04-12 18:19:21 +00:00
if let Err(err) = self.check_index_chunks() {
if repair {
2018-02-24 12:19:51 +00:00
tr_warn!(
2017-07-21 09:21:59 +00:00
"Problem detected: index entries were inconsistent\n\tcaused by: {}",
err
);
2017-04-12 18:19:21 +00:00
return self.rebuild_index();
} else {
2018-02-19 21:30:59 +00:00
return Err(err);
2017-04-12 18:19:21 +00:00
}
}
Ok(())
}
2017-04-10 18:35:28 +00:00
#[inline]
2017-04-12 18:19:21 +00:00
pub fn check_bundles(&mut self, full: bool, repair: bool) -> Result<(), RepositoryError> {
if repair {
try!(self.write_mode());
}
2018-02-24 12:19:51 +00:00
tr_info!("Checking bundle integrity...");
2017-04-12 18:19:21 +00:00
if try!(self.bundles.check(full, repair)) {
// Some bundles got repaired
2018-02-24 12:19:51 +00:00
tr_warn!("Some bundles have been rewritten, please remove the broken bundles manually.");
2017-04-12 18:19:21 +00:00
try!(self.rebuild_bundle_map());
try!(self.rebuild_index());
}
Ok(())
2017-03-20 13:03:29 +00:00
}
2018-03-10 15:35:40 +00:00
pub fn check_repository(&mut self, repair: bool) -> Result<(), RepositoryError> {
tr_info!("Checking repository integrity...");
let mut rebuild = false;
for (_id, bundle_id) in self.bundle_map.bundles() {
if self.bundles.get_bundle_info(&bundle_id).is_none() {
if repair {
tr_warn!(
"Problem detected: bundle map contains unknown bundle {}",
bundle_id
);
rebuild = true;
} else {
return Err(IntegrityError::MissingBundle(bundle_id).into());
}
}
}
if self.bundle_map.len() < self.bundles.len() {
if repair {
tr_warn!("Problem detected: bundle map does not contain all remote bundles");
rebuild = true;
} else {
return Err(IntegrityError::RemoteBundlesNotInMap.into());
}
}
if self.bundle_map.len() > self.bundles.len() {
if repair {
tr_warn!("Problem detected: bundle map contains bundles multiple times");
rebuild = true;
} else {
return Err(IntegrityError::MapContainsDuplicates.into());
}
}
if rebuild {
try!(self.rebuild_bundle_map());
try!(self.rebuild_index());
}
Ok(())
}
2017-03-10 11:43:32 +00:00
}