]>
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 | ||
37 | fn verify_fixed_index(datastore: &DataStore, backup_dir: &BackupDir, info: &FileInfo) -> Result<(), Error> { | |
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() { | |
54 | let (start, end, digest) = index.chunk_info(pos).unwrap(); | |
55 | let size = end - start; | |
56 | datastore.verify_stored_chunk(&digest, size)?; | |
57 | } | |
58 | ||
59 | Ok(()) | |
60 | } | |
61 | ||
62 | fn verify_dynamic_index(datastore: &DataStore, backup_dir: &BackupDir, info: &FileInfo) -> Result<(), Error> { | |
63 | let mut path = backup_dir.relative_path(); | |
64 | path.push(&info.filename); | |
65 | ||
66 | let index = datastore.open_dynamic_reader(&path)?; | |
67 | ||
68 | let (csum, size) = index.compute_csum(); | |
69 | if size != info.size { | |
70 | bail!("wrong size ({} != {})", info.size, size); | |
71 | } | |
72 | ||
73 | if csum != info.csum { | |
74 | bail!("wrong index checksum"); | |
75 | } | |
76 | ||
77 | for pos in 0..index.index_count() { | |
78 | let chunk_info = index.chunk_info(pos).unwrap(); | |
79 | let size = chunk_info.range.end - chunk_info.range.start; | |
80 | datastore.verify_stored_chunk(&chunk_info.digest, size)?; | |
81 | } | |
82 | ||
83 | Ok(()) | |
84 | } | |
85 | ||
86 | /// Verify a single backup snapshot | |
87 | /// | |
88 | /// This checks all archives inside a backup snapshot. | |
89 | /// Errors are logged to the worker log. | |
90 | /// | |
91 | /// Return true if verify is successful | |
92 | pub fn verify_backup_dir(datastore: &DataStore, backup_dir: &BackupDir, worker: &WorkerTask) -> bool { | |
93 | ||
94 | let manifest = match datastore.load_manifest(&backup_dir) { | |
95 | Ok((manifest, _)) => manifest, | |
96 | Err(err) => { | |
97 | worker.log(format!("verify {}:{} - manifest load error: {}", datastore.name(), backup_dir, err)); | |
98 | return false; | |
99 | } | |
100 | }; | |
101 | ||
102 | worker.log(format!("verify {}:{}", datastore.name(), backup_dir)); | |
103 | ||
104 | let mut error_count = 0; | |
105 | ||
106 | for info in manifest.files() { | |
107 | let result = proxmox::try_block!({ | |
108 | worker.log(format!(" check {}", info.filename)); | |
109 | match archive_type(&info.filename)? { | |
110 | ArchiveType::FixedIndex => verify_fixed_index(&datastore, &backup_dir, info), | |
111 | ArchiveType::DynamicIndex => verify_dynamic_index(&datastore, &backup_dir, info), | |
112 | ArchiveType::Blob => verify_blob(&datastore, &backup_dir, info), | |
113 | } | |
114 | }); | |
115 | if let Err(err) = result { | |
116 | worker.log(format!("verify {}:{}/{} failed: {}", datastore.name(), backup_dir, info.filename, err)); | |
117 | error_count += 1; | |
118 | } | |
119 | } | |
120 | ||
121 | error_count == 0 | |
122 | } | |
123 | ||
124 | pub fn verify_backup_group(datastore: &DataStore, group: &BackupGroup, worker: &WorkerTask) -> bool { | |
125 | ||
126 | let mut list = match group.list_backups(&datastore.base_path()) { | |
127 | Ok(list) => list, | |
128 | Err(err) => { | |
129 | worker.log(format!("verify group {}:{} - unable to list backups: {}", datastore.name(), group, err)); | |
130 | return false; | |
131 | } | |
132 | }; | |
133 | ||
134 | worker.log(format!("verify group {}:{}", datastore.name(), group)); | |
135 | ||
136 | let mut error_count = 0; | |
137 | ||
138 | BackupInfo::sort_list(&mut list, false); // newest first | |
139 | for info in list { | |
140 | if !verify_backup_dir(datastore, &info.backup_dir, worker) { | |
141 | error_count += 1; | |
142 | } | |
143 | } | |
144 | ||
145 | error_count == 0 | |
146 | } | |
147 | ||
148 | pub fn verify_all_backups(datastore: &DataStore, worker: &WorkerTask) -> bool { | |
149 | ||
150 | let list = match BackupGroup::list_groups(&datastore.base_path()) { | |
151 | Ok(list) => list, | |
152 | Err(err) => { | |
153 | worker.log(format!("verify datastore {} - unable to list backups: {}", datastore.name(), err)); | |
154 | return false; | |
155 | } | |
156 | }; | |
157 | ||
158 | worker.log(format!("verify datastore {}", datastore.name())); | |
159 | ||
160 | let mut error_count = 0; | |
161 | for group in list { | |
162 | if !verify_backup_group(datastore, &group, worker) { | |
163 | error_count += 1; | |
164 | } | |
165 | } | |
166 | ||
167 | error_count == 0 | |
168 | } |