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
* Backup directories as a thing (list, remove)
* Better control over what is checked in `check` subcommand
* Man pages for all minor subcommands
## 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)_.
The command will perform the following checks in order:
- Bundle integrity
- Bundle integrity (optional)
- Full bundle contents (optional)
- Index integrity
- Index integrity (optional)
- Backup 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
given subtree will be checked in the filesystem integrity check.
Unless `--full` is 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 `--full` is set, the full
bundles are fetched and their contents are compared to what their header claims.
This check takes a long time since all bundles need to fetched, decrypted and
decompressed fully to read their contents.
If `--bundles` is set, the integrity of the bundles will be checked before
checking any backups.
If `--bundle-data` is also set, the full bundles are fetched and their contents
are compared to what their header claims. This check takes a long time since all
bundles need to fetched, decrypted and decompressed fully to read their
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
* `--full`:
* `-b`, `--bundles`:
Check the integrity of the bundles too.
* `--bundle-data`:
Also check the contents of the bundles by fetching and decompressing them.
Note: This flag causes the check to be much slower.
* `-i`, `--index`:
Also check the integrity of the index and its contents.
* `-h`, `--help`:
Prints help information

View File

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

View File

@ -57,7 +57,9 @@ pub enum Arguments {
repo_path: String,
backup_name: Option<String>,
inode: Option<String>,
full: bool
bundles: bool,
bundle_data: bool,
index: bool
},
List {
repo_path: String,
@ -337,7 +339,9 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
.arg(Arg::from_usage("<REPO> 'Path of the repository'")
.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")
.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]]'")
.validator(|val| validate_repo_path(val, true, None, None))))
.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(),
backup_name: backup.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)) => {

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));
}
},
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));
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 {
let backup = try!(get_backup(&repo, &backup_name));
if let Some(path) = inode {
@ -429,7 +436,7 @@ pub fn run() -> Result<(), ErrorCode> {
checked!(repo.check_backup(&backup), "check backup", ErrorCode::CheckRun)
}
} else {
checked!(repo.check(full), "check repository", ErrorCode::CheckRun)
checked!(repo.check_backups(), "check repository", ErrorCode::CheckRun)
}
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> {
let mut new = false;
for &(hash, _len) in chunks {
@ -132,11 +119,13 @@ impl Repository {
}
pub fn check_backup(&mut self, backup: &Backup) -> Result<(), RepositoryError> {
info!("Checking backup...");
let mut checked = Bitmap::new(self.index.capacity());
self.check_subtree(Path::new("").to_path_buf(), &backup.root, &mut checked)
}
pub fn check_inode(&mut self, inode: &Inode, path: &Path) -> Result<(), RepositoryError> {
info!("Checking inode...");
let mut checked = Bitmap::new(self.index.capacity());
try!(self.check_inode_contents(inode, &mut checked));
if let Some(ref children) = inode.children {
@ -147,7 +136,8 @@ impl Repository {
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 backup_map = match self.get_backups() {
Ok(backup_map) => backup_map,
@ -164,7 +154,17 @@ impl Repository {
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() {
if self.bundles.get_bundle_info(&bundle_id).is_none() {
return Err(IntegrityError::MissingBundle(bundle_id).into())
@ -179,20 +179,15 @@ impl Repository {
Ok(())
}
pub fn check(&mut self, full: bool) -> Result<(), RepositoryError> {
try!(self.flush());
info!("Checking bundle integrity...");
try!(self.bundles.check(full));
pub fn check_index(&mut self) -> Result<(), RepositoryError> {
info!("Checking index integrity...");
try!(self.index.check());
info!("Checking bundle map...");
try!(self.check_bundle_map());
info!("Checking index entries...");
try!(self.check_index_chunks());
info!("Checking backup integrity...");
try!(self.check_backups());
info!("Checking repository integrity...");
try!(self.check_repository());
Ok(())
self.check_index_chunks()
}
pub fn check_bundles(&mut self, full: bool) -> Result<(), RepositoryError> {
info!("Checking bundle integrity...");
Ok(try!(self.bundles.check(full)))
}
}