conserve/
validate.rs

1// Copyright 2017-2023 Martin Pool.
2
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation; either version 2 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12
13use std::cmp::max;
14use std::collections::HashMap;
15use std::fmt::Debug;
16use std::sync::Arc;
17
18use tracing::debug;
19
20use crate::monitor::Monitor;
21use crate::*;
22
23/// Options to [Archive::validate].
24#[derive(Debug, Default)]
25pub struct ValidateOptions {
26    /// Assume blocks that are present have the right content: don't read and hash them.
27    pub skip_block_hashes: bool,
28}
29
30/// Validate the indexes of all bands.
31///
32/// Returns the lengths of all blocks that were referenced, so that the caller can check
33/// that all blocks are present and long enough.
34pub(crate) fn validate_bands(
35    archive: &Archive,
36    band_ids: &[BandId],
37    monitor: Arc<dyn Monitor>,
38) -> Result<HashMap<BlockHash, u64>> {
39    let mut block_lens = HashMap::new();
40    let task = monitor.start_task("Validate indexes".to_string());
41    task.set_total(band_ids.len());
42    'band: for band_id in band_ids.iter() {
43        task.increment(1);
44        let band = match Band::open(archive, *band_id) {
45            Ok(band) => band,
46            Err(err) => {
47                monitor.error(err);
48                continue 'band;
49            }
50        };
51        if let Err(err) = band.validate(monitor.clone()) {
52            monitor.error(err);
53            continue 'band;
54        };
55        let st = match archive.open_stored_tree(BandSelectionPolicy::Specified(*band_id)) {
56            Err(err) => {
57                monitor.error(err);
58                continue 'band;
59            }
60            Ok(st) => st,
61        };
62        let band_block_lens = match validate_stored_tree(&st, monitor.clone()) {
63            Err(err) => {
64                monitor.error(err);
65                continue 'band;
66            }
67            Ok(block_lens) => block_lens,
68        };
69        merge_block_lens(&mut block_lens, &band_block_lens);
70    }
71    Ok(block_lens)
72}
73
74fn merge_block_lens(into: &mut HashMap<BlockHash, u64>, from: &HashMap<BlockHash, u64>) {
75    for (bh, bl) in from {
76        into.entry(bh.clone())
77            .and_modify(|l| *l = max(*l, *bl))
78            .or_insert(*bl);
79    }
80}
81
82fn validate_stored_tree(
83    st: &StoredTree,
84    monitor: Arc<dyn Monitor>,
85) -> Result<HashMap<BlockHash, u64>> {
86    // TODO: Check other entry properties are correct.
87    // TODO: Check they're in apath order.
88    // TODO: Count progress for index blocks within one tree?
89    let _task = monitor.start_task(format!("Validate stored tree {}", st.band().id()));
90    let mut block_lens = HashMap::new();
91    for entry in st
92        .iter_entries(Apath::root(), Exclude::nothing(), monitor.clone())?
93        .filter(|entry| entry.kind() == Kind::File)
94    {
95        // TODO: Read index hunks, count into the task per hunk. Then, we can
96        // read hunks in parallel.
97        for addr in entry.addrs {
98            let end = addr.start + addr.len;
99            block_lens
100                .entry(addr.hash.clone())
101                .and_modify(|l| *l = max(*l, end))
102                .or_insert(end);
103        }
104    }
105    debug!(blocks = %block_lens.len(), band_id = ?st.band().id(), "Validated stored tree");
106    Ok(block_lens)
107}