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