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