mirror of https://github.com/dswd/zvault
Dirty flag on error
This commit is contained in:
parent
7efc3de78f
commit
26d73e79a4
2
TODO.md
2
TODO.md
|
@ -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
|
||||||
|
|
|
@ -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} => {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue