lfsx_server/storage/
verify.rs1use futures_util::StreamExt;
2use serde::Serialize;
3use sha2::{Digest, Sha256};
4
5use super::LocalStore;
6use crate::error::Error;
7use crate::namespace::Namespace;
8use crate::oid::Oid;
9
10#[derive(Debug, Default, Serialize, PartialEq, Eq)]
11pub struct VerifyReport {
12 pub checked: u64,
13 pub bytes: u64,
14 pub corrupt: Vec<String>,
15 pub unreadable: Vec<String>,
16 pub incomplete: bool,
20}
21
22impl LocalStore {
23 pub async fn verify(&self, ns: &Namespace) -> Result<VerifyReport, Error> {
29 let walk = self.objects_of(ns).await;
30 let mut report = VerifyReport {
31 incomplete: !walk.complete,
32 ..VerifyReport::default()
33 };
34
35 for found in walk.objects {
36 report.checked += 1;
37
38 match self.digest_of(ns, &found.oid).await {
39 Ok((digest, read)) => {
40 report.bytes += read;
41
42 if digest != found.oid.as_str() {
43 report.corrupt.push(found.oid.to_string());
44 }
45 }
46 Err(error) => {
50 tracing::warn!(oid = %found.oid, %error, "object could not be read");
51 report.unreadable.push(found.oid.to_string());
52 }
53 }
54 }
55
56 Ok(report)
57 }
58
59 async fn digest_of(&self, ns: &Namespace, oid: &Oid) -> Result<(String, u64), Error> {
60 let object = self.open(ns, oid).await?;
61 let size = object.size();
62
63 let mut hasher = Sha256::new();
64 let mut read = 0u64;
65 let mut chunks = object.stream(0, size).await?;
66
67 while let Some(chunk) = chunks.next().await {
68 let chunk = chunk?;
69 read += chunk.len() as u64;
70 hasher.update(&chunk);
71 }
72
73 Ok((hex::encode(hasher.finalize()), read))
74 }
75}
76
77#[cfg(test)]
78mod tests;