From 5101ead24cdabb8606da3a3b89901d5ec00c5bbd Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Wed, 9 Feb 2022 08:49:20 +0100 Subject: [PATCH] add check command --- src/commands/check.rs | 111 ++++++++++++++++++++++++++++++++++++++++++ src/commands/mod.rs | 5 ++ src/id.rs | 4 ++ 3 files changed, 120 insertions(+) create mode 100644 src/commands/check.rs diff --git a/src/commands/check.rs b/src/commands/check.rs new file mode 100644 index 0000000..9d6f895 --- /dev/null +++ b/src/commands/check.rs @@ -0,0 +1,111 @@ +use anyhow::Result; +use clap::Parser; +use std::collections::HashMap; + +use crate::backend::{FileType, ReadBackend}; +use crate::blob::tree_iterator_once; +use crate::index::{AllIndexFiles, BoomIndex, ReadIndex}; +use crate::repo::{IndexBlob, SnapshotFile}; + +#[derive(Parser)] +pub(super) struct Opts { + /// read all data blobs + #[clap(long)] + read_data: bool, +} + +pub(super) fn execute(be: &impl ReadBackend, opts: Opts) -> Result<()> { + println!("checking packs..."); + check_packs(be)?; + + println!("loading index..."); + let index = BoomIndex::from_iter(AllIndexFiles::new(be.clone()).into_iter()); + + println!("checking snapshots and trees..."); + check_snapshots(be, &index)?; + + if opts.read_data { + unimplemented!() + } + + Ok(()) +} + +// calculate the pack size from the contained blobs +fn pack_size(blobs: &[IndexBlob]) -> u32 { + let mut size = 4 + 32; // 4 + crypto overhead + for blob in blobs { + size += blob.length() + 37 // 37 = length of blob description + } + size +} + +// check if packs correspond to index +fn check_packs(be: &impl ReadBackend) -> Result<()> { + let mut packs = AllIndexFiles::new(be.clone()) + .into_iter() + .map(|p| (*p.id(), pack_size(p.blobs()))) + .collect::>(); + + for (id, size) in be.list_with_size(FileType::Pack)? { + match packs.remove(&id) { + None => println!("pack {} not contained in index", id.to_hex()), + Some(index_size) if index_size != size => println!( + "pack {}: size computed by index: {}, actual size: {}", + id.to_hex(), + index_size, + size + ), + _ => {} //everything ok + } + } + + for (id, _) in packs { + println!( + "pack {} is referenced by the index but not presend!", + id.to_hex() + ); + } + + Ok(()) +} + +// check if all snapshots and contained trees can be loaded and contents exist in the index +fn check_snapshots(be: &impl ReadBackend, index: &impl ReadIndex) -> Result<()> { + let snap_ids = be + .list(FileType::Snapshot)? + .into_iter() + .map(|id| SnapshotFile::from_backend(be, id).unwrap().tree) + .collect(); + + for path_node in tree_iterator_once(be, index, snap_ids) { + let fullname = path_node.path().join(path_node.node().name()); + let node = path_node.node(); + + match node.tpe() as &str { + "file" => { + for (i, id) in node.content().iter().enumerate() { + if id.is_null() { + println!("file {:?} blob {} has null ID", fullname, i); + } + + if index.get_id(id).is_none() { + println!("file {:?} blob {} is missig in index", fullname, id); + } + } + } + + "dir" => { + if node.subtree().is_null() { + println!("dir {:?} subtree has null ID", fullname); + } + } + + "symlink" | "socket" | "chardev" | "dev" | "fifo" => {} // nothing to check + + tpe => println!("file {:?} unkown type {}", fullname, tpe), + } + } + + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 91a53f5..bf6c7e3 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -7,6 +7,7 @@ use crate::backend::{DecryptBackend, LocalBackend}; use crate::repo; mod cat; +mod check; mod list; mod ls; mod snapshots; @@ -39,6 +40,9 @@ enum Command { /// ls snapshots Ls(ls::Opts), + + /// check repository + Check(check::Opts), } pub fn execute() -> Result<()> { @@ -54,5 +58,6 @@ pub fn execute() -> Result<()> { Command::Cat(opts) => cat::execute(&be, &dbe, opts), Command::Snapshots(opts) => snapshots::execute(&dbe, opts), Command::Ls(opts) => ls::execute(&dbe, opts), + Command::Check(opts) => check::execute(&dbe, opts), } } diff --git a/src/id.rs b/src/id.rs index 6f04657..6343260 100644 --- a/src/id.rs +++ b/src/id.rs @@ -33,4 +33,8 @@ impl Id { pub fn to_hex(self) -> String { hex::encode(self.0) } + + pub fn is_null(self) -> bool { + self == Id::default() + } }