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