]> git.proxmox.com Git - proxmox-backup.git/blame - src/backup/verify.rs
verify: check all chunks of an index, even if we encounter a corrupt one
[proxmox-backup.git] / src / backup / verify.rs
CommitLineData
2aaae970
DM
1use std::collections::HashSet;
2
c2009e53
DM
3use anyhow::{bail, Error};
4
5use crate::server::WorkerTask;
6
7use super::{
8 DataStore, BackupGroup, BackupDir, BackupInfo, IndexFile,
9 ENCR_COMPR_BLOB_MAGIC_1_0, ENCRYPTED_BLOB_MAGIC_1_0,
10 FileInfo, ArchiveType, archive_type,
11};
12
13fn verify_blob(datastore: &DataStore, backup_dir: &BackupDir, info: &FileInfo) -> Result<(), Error> {
14
39f18b30 15 let blob = datastore.load_blob(backup_dir, &info.filename)?;
c2009e53 16
2aaae970 17 let raw_size = blob.raw_size();
c2009e53
DM
18 if raw_size != info.size {
19 bail!("wrong size ({} != {})", info.size, raw_size);
20 }
21
39f18b30 22 let csum = openssl::sha::sha256(blob.raw_data());
c2009e53
DM
23 if csum != info.csum {
24 bail!("wrong index checksum");
25 }
26
c2009e53
DM
27 let magic = blob.magic();
28
29 if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 || magic == &ENCRYPTED_BLOB_MAGIC_1_0 {
30 return Ok(());
31 }
32
33 blob.decode(None)?;
34
35 Ok(())
36}
37
fdaab0df
DM
38fn verify_index_chunks(
39 datastore: &DataStore,
40 index: Box<dyn IndexFile>,
2aaae970 41 verified_chunks: &mut HashSet<[u8;32]>,
fdaab0df
DM
42 worker: &WorkerTask,
43) -> Result<(), Error> {
44
f66f537d 45 let mut errors = 0;
fdaab0df
DM
46 for pos in 0..index.index_count() {
47
48 worker.fail_on_abort()?;
fdaab0df
DM
49
50 let info = index.chunk_info(pos).unwrap();
51 let size = info.range.end - info.range.start;
2aaae970
DM
52
53 if !verified_chunks.contains(&info.digest) {
f66f537d
DC
54 if let Err(err) = datastore.verify_stored_chunk(&info.digest, size) {
55 worker.log(format!("{}", err));
56 errors += 1;
57 } else {
58 verified_chunks.insert(info.digest);
59 }
2aaae970 60 }
fdaab0df
DM
61 }
62
f66f537d
DC
63 if errors > 0 {
64 bail!("chunks could not be verified");
65 }
66
fdaab0df
DM
67 Ok(())
68}
69
2aaae970
DM
70fn verify_fixed_index(
71 datastore: &DataStore,
72 backup_dir: &BackupDir,
73 info: &FileInfo,
74 verified_chunks: &mut HashSet<[u8;32]>,
75 worker: &WorkerTask,
76) -> Result<(), Error> {
c2009e53
DM
77
78 let mut path = backup_dir.relative_path();
79 path.push(&info.filename);
80
81 let index = datastore.open_fixed_reader(&path)?;
82
83 let (csum, size) = index.compute_csum();
84 if size != info.size {
85 bail!("wrong size ({} != {})", info.size, size);
86 }
87
88 if csum != info.csum {
89 bail!("wrong index checksum");
90 }
91
2aaae970 92 verify_index_chunks(datastore, Box::new(index), verified_chunks, worker)
c2009e53
DM
93}
94
2aaae970
DM
95fn verify_dynamic_index(
96 datastore: &DataStore,
97 backup_dir: &BackupDir,
98 info: &FileInfo,
99 verified_chunks: &mut HashSet<[u8;32]>,
100 worker: &WorkerTask,
101) -> Result<(), Error> {
102
c2009e53
DM
103 let mut path = backup_dir.relative_path();
104 path.push(&info.filename);
105
106 let index = datastore.open_dynamic_reader(&path)?;
107
108 let (csum, size) = index.compute_csum();
109 if size != info.size {
110 bail!("wrong size ({} != {})", info.size, size);
111 }
112
113 if csum != info.csum {
114 bail!("wrong index checksum");
115 }
116
2aaae970 117 verify_index_chunks(datastore, Box::new(index), verified_chunks, worker)
c2009e53
DM
118}
119
120/// Verify a single backup snapshot
121///
122/// This checks all archives inside a backup snapshot.
123/// Errors are logged to the worker log.
124///
8ea00f6e
DM
125/// Returns
126/// - Ok(true) if verify is successful
127/// - Ok(false) if there were verification errors
128/// - Err(_) if task was aborted
2aaae970
DM
129pub fn verify_backup_dir(
130 datastore: &DataStore,
131 backup_dir: &BackupDir,
132 verified_chunks: &mut HashSet<[u8;32]>,
133 worker: &WorkerTask
134) -> Result<bool, Error> {
c2009e53
DM
135
136 let manifest = match datastore.load_manifest(&backup_dir) {
521a0acb 137 Ok((manifest, _crypt_mode, _)) => manifest,
c2009e53
DM
138 Err(err) => {
139 worker.log(format!("verify {}:{} - manifest load error: {}", datastore.name(), backup_dir, err));
8ea00f6e 140 return Ok(false);
c2009e53
DM
141 }
142 };
143
144 worker.log(format!("verify {}:{}", datastore.name(), backup_dir));
145
146 let mut error_count = 0;
147
148 for info in manifest.files() {
149 let result = proxmox::try_block!({
150 worker.log(format!(" check {}", info.filename));
151 match archive_type(&info.filename)? {
2aaae970
DM
152 ArchiveType::FixedIndex => verify_fixed_index(&datastore, &backup_dir, info, verified_chunks, worker),
153 ArchiveType::DynamicIndex => verify_dynamic_index(&datastore, &backup_dir, info, verified_chunks, worker),
c2009e53
DM
154 ArchiveType::Blob => verify_blob(&datastore, &backup_dir, info),
155 }
156 });
8ea00f6e
DM
157
158 worker.fail_on_abort()?;
8ea00f6e 159
c2009e53
DM
160 if let Err(err) = result {
161 worker.log(format!("verify {}:{}/{} failed: {}", datastore.name(), backup_dir, info.filename, err));
162 error_count += 1;
163 }
164 }
165
8ea00f6e 166 Ok(error_count == 0)
c2009e53
DM
167}
168
8ea00f6e
DM
169/// Verify all backups inside a backup group
170///
171/// Errors are logged to the worker log.
172///
173/// Returns
174/// - Ok(true) if verify is successful
175/// - Ok(false) if there were verification errors
176/// - Err(_) if task was aborted
177pub fn verify_backup_group(datastore: &DataStore, group: &BackupGroup, worker: &WorkerTask) -> Result<bool, Error> {
c2009e53
DM
178
179 let mut list = match group.list_backups(&datastore.base_path()) {
180 Ok(list) => list,
181 Err(err) => {
182 worker.log(format!("verify group {}:{} - unable to list backups: {}", datastore.name(), group, err));
8ea00f6e 183 return Ok(false);
c2009e53
DM
184 }
185 };
186
187 worker.log(format!("verify group {}:{}", datastore.name(), group));
188
189 let mut error_count = 0;
190
2aaae970
DM
191 let mut verified_chunks = HashSet::with_capacity(1024*16); // start with 16384 chunks (up to 65GB)
192
c2009e53
DM
193 BackupInfo::sort_list(&mut list, false); // newest first
194 for info in list {
2aaae970 195 if !verify_backup_dir(datastore, &info.backup_dir, &mut verified_chunks, worker)? {
c2009e53
DM
196 error_count += 1;
197 }
198 }
199
8ea00f6e 200 Ok(error_count == 0)
c2009e53
DM
201}
202
8ea00f6e
DM
203/// Verify all backups inside a datastore
204///
205/// Errors are logged to the worker log.
206///
207/// Returns
208/// - Ok(true) if verify is successful
209/// - Ok(false) if there were verification errors
210/// - Err(_) if task was aborted
211pub fn verify_all_backups(datastore: &DataStore, worker: &WorkerTask) -> Result<bool, Error> {
c2009e53
DM
212
213 let list = match BackupGroup::list_groups(&datastore.base_path()) {
214 Ok(list) => list,
215 Err(err) => {
216 worker.log(format!("verify datastore {} - unable to list backups: {}", datastore.name(), err));
8ea00f6e 217 return Ok(false);
c2009e53
DM
218 }
219 };
220
221 worker.log(format!("verify datastore {}", datastore.name()));
222
223 let mut error_count = 0;
224 for group in list {
8ea00f6e 225 if !verify_backup_group(datastore, &group, worker)? {
c2009e53
DM
226 error_count += 1;
227 }
228 }
229
8ea00f6e 230 Ok(error_count == 0)
c2009e53 231}