Dirty flag on error

This commit is contained in:
Dennis Schwerdel 2017-04-10 20:15:13 +02:00 committed by Dennis Schwerdel
parent 7efc3de78f
commit 26d73e79a4
9 changed files with 47 additions and 2 deletions

View File

@ -3,7 +3,7 @@
## Functionality ## Functionality
* XAttrs in fuse * XAttrs in fuse
* XAttrs in tar * XAttrs in tar
* `check --repair` * Repair
## Stability / Reliability ## Stability / Reliability
* Lock the local repository to avoid index corruption * Lock the local repository to avoid index corruption

View File

@ -438,6 +438,7 @@ pub fn run() -> Result<(), ErrorCode> {
} else { } else {
checked!(repo.check_backups(), "check repository", ErrorCode::CheckRun) checked!(repo.check_backups(), "check repository", ErrorCode::CheckRun)
} }
repo.set_clean();
info!("Integrity verified") info!("Integrity verified")
}, },
Arguments::List{repo_path, backup_name, inode} => { Arguments::List{repo_path, backup_name, inode} => {

View File

@ -225,6 +225,10 @@ impl Repository {
#[allow(dead_code)] #[allow(dead_code)]
pub fn create_backup_recursively<P: AsRef<Path>>(&mut self, path: P, reference: Option<&Backup>, options: &BackupOptions) -> Result<Backup, RepositoryError> { pub fn create_backup_recursively<P: AsRef<Path>>(&mut self, path: P, reference: Option<&Backup>, options: &BackupOptions) -> Result<Backup, RepositoryError> {
let _lock = try!(self.lock(false)); let _lock = try!(self.lock(false));
if self.dirty {
return Err(RepositoryError::Dirty)
}
self.dirty = true;
let reference_inode = reference.and_then(|b| self.get_inode(&b.root).ok()); let reference_inode = reference.and_then(|b| self.get_inode(&b.root).ok());
let mut backup = Backup::default(); let mut backup = Backup::default();
backup.config = self.config.clone(); backup.config = self.config.clone();
@ -251,6 +255,7 @@ impl Repository {
backup.bundle_count = info_after.bundle_count - info_before.bundle_count; backup.bundle_count = info_after.bundle_count - info_before.bundle_count;
backup.chunk_count = info_after.chunk_count - info_before.chunk_count; backup.chunk_count = info_after.chunk_count - info_before.chunk_count;
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;
self.dirty = false;
if failed_paths.is_empty() { if failed_paths.is_empty() {
Ok(backup) Ok(backup)
} else { } else {

View File

@ -78,6 +78,10 @@ quick_error!{
description("Integrity error") description("Integrity error")
display("Repository error: integrity error\n\tcaused by: {}", err) display("Repository error: integrity error\n\tcaused by: {}", err)
} }
Dirty {
description("Dirty repository")
display("The repository is dirty, please run a check")
}
Backup(err: BackupError) { Backup(err: BackupError) {
from() from()
cause(err) cause(err)

View File

@ -61,6 +61,10 @@ impl Repository {
} }
pub fn analyze_usage(&mut self) -> Result<HashMap<u32, BundleAnalysis>, RepositoryError> { pub fn analyze_usage(&mut self) -> Result<HashMap<u32, BundleAnalysis>, RepositoryError> {
if self.dirty {
return Err(RepositoryError::Dirty)
}
self.dirty = true;
let mut usage = HashMap::new(); let mut usage = HashMap::new();
for (id, bundle) in self.bundle_map.bundles() { for (id, bundle) in self.bundle_map.bundles() {
let bundle = try!(self.bundles.get_bundle_info(&bundle).ok_or_else(|| IntegrityError::MissingBundle(bundle))); let bundle = try!(self.bundles.get_bundle_info(&bundle).ok_or_else(|| IntegrityError::MissingBundle(bundle)));
@ -101,6 +105,7 @@ impl Repository {
} }
} }
} }
self.dirty = false;
Ok(usage) Ok(usage)
} }

View File

@ -102,4 +102,8 @@ impl RepositoryLayout {
pub fn remote_bundle_cache_path(&self) -> PathBuf { pub fn remote_bundle_cache_path(&self) -> PathBuf {
self.0.join("bundles/remote.cache") self.0.join("bundles/remote.cache")
} }
pub fn dirtyfile_path(&self) -> PathBuf {
self.0.join("dirty")
}
} }

View File

@ -48,7 +48,8 @@ pub struct Repository {
data_bundle: Option<BundleWriter>, data_bundle: Option<BundleWriter>,
meta_bundle: Option<BundleWriter>, meta_bundle: Option<BundleWriter>,
chunker: Chunker, chunker: Chunker,
locks: LockFolder locks: LockFolder,
dirty: bool
} }
@ -92,8 +93,10 @@ impl Repository {
(BundleMap::create(), true) (BundleMap::create(), true)
} }
}; };
let dirty = layout.dirtyfile_path().exists();
let mut repo = Repository { let mut repo = Repository {
layout: layout, layout: layout,
dirty: true,
chunker: config.chunker.create(), chunker: config.chunker.create(),
config: config, config: config,
index: index, index: index,
@ -128,6 +131,7 @@ impl Repository {
if rebuild_index { if rebuild_index {
try!(repo.rebuild_index()); try!(repo.rebuild_index());
} }
repo.dirty = dirty;
Ok(repo) Ok(repo)
} }
@ -190,6 +194,10 @@ impl Repository {
} }
pub fn flush(&mut self) -> Result<(), RepositoryError> { pub fn flush(&mut self) -> Result<(), RepositoryError> {
let dirtyfile = self.layout.dirtyfile_path();
if self.dirty && !dirtyfile.exists() {
try!(File::create(&dirtyfile));
}
if self.data_bundle.is_some() { if self.data_bundle.is_some() {
let mut finished = None; let mut finished = None;
mem::swap(&mut self.data_bundle, &mut finished); mem::swap(&mut self.data_bundle, &mut finished);
@ -211,6 +219,9 @@ impl Repository {
try!(self.bundles.finish_uploads()); try!(self.bundles.finish_uploads());
try!(self.save_bundle_map()); try!(self.save_bundle_map());
try!(self.bundles.save_cache()); try!(self.bundles.save_cache());
if !self.dirty && dirtyfile.exists() {
try!(fs::remove_file(&dirtyfile));
}
Ok(()) Ok(())
} }
@ -281,6 +292,10 @@ impl Repository {
fn lock(&self, exclusive: bool) -> Result<LockHandle, RepositoryError> { fn lock(&self, exclusive: bool) -> Result<LockHandle, RepositoryError> {
Ok(try!(self.locks.lock(exclusive))) Ok(try!(self.locks.lock(exclusive)))
} }
pub fn set_clean(&mut self) {
self.dirty = false;
}
} }

View File

@ -155,6 +155,10 @@ impl Repository {
pub fn import_tarfile<P: AsRef<Path>>(&mut self, tarfile: P) -> Result<Backup, RepositoryError> { pub fn import_tarfile<P: AsRef<Path>>(&mut self, tarfile: P) -> Result<Backup, RepositoryError> {
let _lock = try!(self.lock(false)); let _lock = try!(self.lock(false));
if self.dirty {
return Err(RepositoryError::Dirty)
}
self.dirty = true;
let mut backup = Backup::default(); let mut backup = Backup::default();
backup.config = self.config.clone(); backup.config = self.config.clone();
backup.host = get_hostname().unwrap_or_else(|_| "".to_string()); backup.host = get_hostname().unwrap_or_else(|_| "".to_string());
@ -177,6 +181,7 @@ impl Repository {
backup.bundle_count = info_after.bundle_count - info_before.bundle_count; backup.bundle_count = info_after.bundle_count - info_before.bundle_count;
backup.chunk_count = info_after.chunk_count - info_before.chunk_count; backup.chunk_count = info_after.chunk_count - info_before.chunk_count;
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;
self.dirty = false;
if failed_paths.is_empty() { if failed_paths.is_empty() {
Ok(backup) Ok(backup)
} else { } else {

View File

@ -17,6 +17,10 @@ impl Repository {
try!(self.flush()); try!(self.flush());
info!("Locking repository"); info!("Locking repository");
let _lock = try!(self.lock(true)); let _lock = try!(self.lock(true));
if self.dirty {
return Err(RepositoryError::Dirty)
}
self.dirty = true;
info!("Analyzing chunk usage"); info!("Analyzing chunk usage");
let usage = try!(self.analyze_usage()); let usage = try!(self.analyze_usage());
let mut data_total = 0; let mut data_total = 0;
@ -36,6 +40,7 @@ impl Repository {
} }
info!("Reclaiming {} by rewriting {} bundles", to_file_size(reclaim_space as u64), rewrite_bundles.len()); info!("Reclaiming {} by rewriting {} bundles", to_file_size(reclaim_space as u64), rewrite_bundles.len());
if !force { if !force {
self.dirty = false;
return Ok(()) return Ok(())
} }
for id in &rewrite_bundles { for id in &rewrite_bundles {
@ -65,6 +70,7 @@ impl Repository {
try!(self.delete_bundle(id)); try!(self.delete_bundle(id));
} }
try!(self.save_bundle_map()); try!(self.save_bundle_map());
self.dirty = false;
Ok(()) Ok(())
} }
} }