Better control over what is checked in `check` subcommand

This commit is contained in:
Dennis Schwerdel 2017-04-10 19:28:17 +02:00 committed by Dennis Schwerdel
parent daf043dccf
commit 7efc3de78f
6 changed files with 65 additions and 43 deletions

View File

@ -10,7 +10,6 @@
## Usability ## Usability
* Backup directories as a thing (list, remove) * Backup directories as a thing (list, remove)
* Better control over what is checked in `check` subcommand
* Man pages for all minor subcommands * Man pages for all minor subcommands
## Code quality ## Code quality

View File

@ -15,9 +15,9 @@ The repository, backup, of subtree given by `PATH` must be in the format
`[repository][::backup_name[::subtree]]` as described in _zvault(1)_. `[repository][::backup_name[::subtree]]` as described in _zvault(1)_.
The command will perform the following checks in order: The command will perform the following checks in order:
- Bundle integrity - Bundle integrity (optional)
- Full bundle contents (optional) - Full bundle contents (optional)
- Index integrity - Index integrity (optional)
- Backup integrity - Backup integrity
- Filesystem integrity - Filesystem integrity
@ -28,22 +28,37 @@ in the filesystem integrity check.
If a subtree is specified in `PATH`, no backups will be checked and only the If a subtree is specified in `PATH`, no backups will be checked and only the
given subtree will be checked in the filesystem integrity check. given subtree will be checked in the filesystem integrity check.
Unless `--full` is set, the bundles will only be checked without actually If `--bundles` is set, the integrity of the bundles will be checked before
fetching them fully. This means that their contents can only be read from their checking any backups.
header and this information is not verified. If `--full` is set, the full If `--bundle-data` is also set, the full bundles are fetched and their contents
bundles are fetched and their contents are compared to what their header claims. are compared to what their header claims. This check takes a long time since all
This check takes a long time since all bundles need to fetched, decrypted and bundles need to fetched, decrypted and decompressed fully to read their
decompressed fully to read their contents. contents. If this flag is not set, the bundles will only be checked without
actually fetching them fully. This means that their contents can only be read
from their header and this information is not verified.
If `--index` is set, the integrity of the index and its contents will be checked
before checking any backups.
## OPTIONS ## OPTIONS
* `--full`: * `-b`, `--bundles`:
Check the integrity of the bundles too.
* `--bundle-data`:
Also check the contents of the bundles by fetching and decompressing them. Also check the contents of the bundles by fetching and decompressing them.
Note: This flag causes the check to be much slower. Note: This flag causes the check to be much slower.
* `-i`, `--index`:
Also check the integrity of the index and its contents.
* `-h`, `--help`: * `-h`, `--help`:
Prints help information Prints help information

View File

@ -281,7 +281,7 @@ impl BundleDb {
#[inline] #[inline]
pub fn get_bundle_info(&self, bundle: &BundleId) -> Option<&StoredBundle> { pub fn get_bundle_info(&self, bundle: &BundleId) -> Option<&StoredBundle> {
self.get_stored_bundle(bundle).ok() self.remote_bundles.get(bundle)
} }
#[inline] #[inline]

View File

@ -57,7 +57,9 @@ pub enum Arguments {
repo_path: String, repo_path: String,
backup_name: Option<String>, backup_name: Option<String>,
inode: Option<String>, inode: Option<String>,
full: bool bundles: bool,
bundle_data: bool,
index: bool
}, },
List { List {
repo_path: String, repo_path: String,
@ -337,7 +339,9 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
.arg(Arg::from_usage("<REPO> 'Path of the repository'") .arg(Arg::from_usage("<REPO> 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false))))) .validator(|val| validate_repo_path(val, true, Some(false), Some(false)))))
.subcommand(SubCommand::with_name("check").about("Check the repository, a backup or a backup subtree") .subcommand(SubCommand::with_name("check").about("Check the repository, a backup or a backup subtree")
.arg(Arg::from_usage("--full 'Also check file contents (slow)'")) .arg(Arg::from_usage("-b --bundles 'Check the bundles'"))
.arg(Arg::from_usage("[bundle_data] --bundle-data 'Check bundle contents (slow)'").requires("bundles").alias("--data"))
.arg(Arg::from_usage("-i --index 'Check the chunk index'"))
.arg(Arg::from_usage("<PATH> 'Path of the repository/backup/subtree, [repository][::backup[::subtree]]'") .arg(Arg::from_usage("<PATH> 'Path of the repository/backup/subtree, [repository][::backup[::subtree]]'")
.validator(|val| validate_repo_path(val, true, None, None)))) .validator(|val| validate_repo_path(val, true, None, None))))
.subcommand(SubCommand::with_name("list").alias("ls").about("List backups or backup contents") .subcommand(SubCommand::with_name("list").alias("ls").about("List backups or backup contents")
@ -491,7 +495,9 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
repo_path: repository.to_string(), repo_path: repository.to_string(),
backup_name: backup.map(|v| v.to_string()), backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string()), inode: inode.map(|v| v.to_string()),
full: args.is_present("full") bundles: args.is_present("bundles"),
bundle_data: args.is_present("bundle_data"),
index: args.is_present("index")
} }
}, },
("list", Some(args)) => { ("list", Some(args)) => {

View File

@ -418,8 +418,15 @@ pub fn run() -> Result<(), ErrorCode> {
info!("Reclaimed {}", to_file_size(info_before.encoded_data_size - info_after.encoded_data_size)); info!("Reclaimed {}", to_file_size(info_before.encoded_data_size - info_after.encoded_data_size));
} }
}, },
Arguments::Check{repo_path, backup_name, inode, full} => { Arguments::Check{repo_path, backup_name, inode, bundles, index, bundle_data} => {
let mut repo = try!(open_repository(&repo_path)); let mut repo = try!(open_repository(&repo_path));
checked!(repo.check_repository(), "check repository", ErrorCode::CheckRun);
if bundles {
checked!(repo.check_bundles(bundle_data), "check bundles", ErrorCode::CheckRun);
}
if index {
checked!(repo.check_index(), "check index", ErrorCode::CheckRun);
}
if let Some(backup_name) = backup_name { if let Some(backup_name) = backup_name {
let backup = try!(get_backup(&repo, &backup_name)); let backup = try!(get_backup(&repo, &backup_name));
if let Some(path) = inode { if let Some(path) = inode {
@ -429,7 +436,7 @@ pub fn run() -> Result<(), ErrorCode> {
checked!(repo.check_backup(&backup), "check backup", ErrorCode::CheckRun) checked!(repo.check_backup(&backup), "check backup", ErrorCode::CheckRun)
} }
} else { } else {
checked!(repo.check(full), "check repository", ErrorCode::CheckRun) checked!(repo.check_backups(), "check repository", ErrorCode::CheckRun)
} }
info!("Integrity verified") info!("Integrity verified")
}, },

View File

@ -64,19 +64,6 @@ impl Repository {
}) })
} }
fn check_repository(&self) -> Result<(), RepositoryError> {
if self.next_data_bundle == self.next_meta_bundle {
return Err(IntegrityError::InvalidNextBundleId.into())
}
if self.bundle_map.get(self.next_data_bundle).is_some() {
return Err(IntegrityError::InvalidNextBundleId.into())
}
if self.bundle_map.get(self.next_meta_bundle).is_some() {
return Err(IntegrityError::InvalidNextBundleId.into())
}
Ok(())
}
fn check_chunks(&self, checked: &mut Bitmap, chunks: &[Chunk]) -> Result<bool, RepositoryError> { fn check_chunks(&self, checked: &mut Bitmap, chunks: &[Chunk]) -> Result<bool, RepositoryError> {
let mut new = false; let mut new = false;
for &(hash, _len) in chunks { for &(hash, _len) in chunks {
@ -132,11 +119,13 @@ impl Repository {
} }
pub fn check_backup(&mut self, backup: &Backup) -> Result<(), RepositoryError> { pub fn check_backup(&mut self, backup: &Backup) -> Result<(), RepositoryError> {
info!("Checking backup...");
let mut checked = Bitmap::new(self.index.capacity()); let mut checked = Bitmap::new(self.index.capacity());
self.check_subtree(Path::new("").to_path_buf(), &backup.root, &mut checked) self.check_subtree(Path::new("").to_path_buf(), &backup.root, &mut checked)
} }
pub fn check_inode(&mut self, inode: &Inode, path: &Path) -> Result<(), RepositoryError> { pub fn check_inode(&mut self, inode: &Inode, path: &Path) -> Result<(), RepositoryError> {
info!("Checking inode...");
let mut checked = Bitmap::new(self.index.capacity()); let mut checked = Bitmap::new(self.index.capacity());
try!(self.check_inode_contents(inode, &mut checked)); try!(self.check_inode_contents(inode, &mut checked));
if let Some(ref children) = inode.children { if let Some(ref children) = inode.children {
@ -147,7 +136,8 @@ impl Repository {
Ok(()) Ok(())
} }
fn check_backups(&mut self) -> Result<(), RepositoryError> { pub fn check_backups(&mut self) -> Result<(), RepositoryError> {
info!("Checking backups...");
let mut checked = Bitmap::new(self.index.capacity()); let mut checked = Bitmap::new(self.index.capacity());
let backup_map = match self.get_backups() { let backup_map = match self.get_backups() {
Ok(backup_map) => backup_map, Ok(backup_map) => backup_map,
@ -164,7 +154,17 @@ impl Repository {
Ok(()) Ok(())
} }
fn check_bundle_map(&mut self) -> Result<(), RepositoryError> { pub fn check_repository(&mut self) -> Result<(), RepositoryError> {
info!("Checking repository integrity...");
if self.next_data_bundle == self.next_meta_bundle {
return Err(IntegrityError::InvalidNextBundleId.into())
}
if self.bundle_map.get(self.next_data_bundle).is_some() {
return Err(IntegrityError::InvalidNextBundleId.into())
}
if self.bundle_map.get(self.next_meta_bundle).is_some() {
return Err(IntegrityError::InvalidNextBundleId.into())
}
for (_id, bundle_id) in self.bundle_map.bundles() { for (_id, bundle_id) in self.bundle_map.bundles() {
if self.bundles.get_bundle_info(&bundle_id).is_none() { if self.bundles.get_bundle_info(&bundle_id).is_none() {
return Err(IntegrityError::MissingBundle(bundle_id).into()) return Err(IntegrityError::MissingBundle(bundle_id).into())
@ -179,20 +179,15 @@ impl Repository {
Ok(()) Ok(())
} }
pub fn check(&mut self, full: bool) -> Result<(), RepositoryError> { pub fn check_index(&mut self) -> Result<(), RepositoryError> {
try!(self.flush());
info!("Checking bundle integrity...");
try!(self.bundles.check(full));
info!("Checking index integrity..."); info!("Checking index integrity...");
try!(self.index.check()); try!(self.index.check());
info!("Checking bundle map...");
try!(self.check_bundle_map());
info!("Checking index entries..."); info!("Checking index entries...");
try!(self.check_index_chunks()); self.check_index_chunks()
info!("Checking backup integrity..."); }
try!(self.check_backups());
info!("Checking repository integrity..."); pub fn check_bundles(&mut self, full: bool) -> Result<(), RepositoryError> {
try!(self.check_repository()); info!("Checking bundle integrity...");
Ok(()) Ok(try!(self.bundles.check(full)))
} }
} }