1 use std
::collections
::HashSet
;
3 use anyhow
::{bail, Error}
;
5 use crate::server
::WorkerTask
;
8 DataStore
, BackupGroup
, BackupDir
, BackupInfo
, IndexFile
,
9 ENCR_COMPR_BLOB_MAGIC_1_0
, ENCRYPTED_BLOB_MAGIC_1_0
,
10 FileInfo
, ArchiveType
, archive_type
,
13 fn verify_blob(datastore
: &DataStore
, backup_dir
: &BackupDir
, info
: &FileInfo
) -> Result
<(), Error
> {
15 let blob
= datastore
.load_blob(backup_dir
, &info
.filename
)?
;
17 let raw_size
= blob
.raw_size();
18 if raw_size
!= info
.size
{
19 bail
!("wrong size ({} != {})", info
.size
, raw_size
);
22 let csum
= openssl
::sha
::sha256(blob
.raw_data());
23 if csum
!= info
.csum
{
24 bail
!("wrong index checksum");
27 let magic
= blob
.magic();
29 if magic
== &ENCR_COMPR_BLOB_MAGIC_1_0
|| magic
== &ENCRYPTED_BLOB_MAGIC_1_0
{
38 fn verify_index_chunks(
39 datastore
: &DataStore
,
40 index
: Box
<dyn IndexFile
>,
41 verified_chunks
: &mut HashSet
<[u8;32]>,
43 ) -> Result
<(), Error
> {
46 for pos
in 0..index
.index_count() {
48 worker
.fail_on_abort()?
;
50 let info
= index
.chunk_info(pos
).unwrap();
51 let size
= info
.range
.end
- info
.range
.start
;
53 if !verified_chunks
.contains(&info
.digest
) {
54 if let Err(err
) = datastore
.verify_stored_chunk(&info
.digest
, size
) {
55 worker
.log(format
!("{}", err
));
58 verified_chunks
.insert(info
.digest
);
64 bail
!("chunks could not be verified");
70 fn verify_fixed_index(
71 datastore
: &DataStore
,
72 backup_dir
: &BackupDir
,
74 verified_chunks
: &mut HashSet
<[u8;32]>,
76 ) -> Result
<(), Error
> {
78 let mut path
= backup_dir
.relative_path();
79 path
.push(&info
.filename
);
81 let index
= datastore
.open_fixed_reader(&path
)?
;
83 let (csum
, size
) = index
.compute_csum();
84 if size
!= info
.size
{
85 bail
!("wrong size ({} != {})", info
.size
, size
);
88 if csum
!= info
.csum
{
89 bail
!("wrong index checksum");
92 verify_index_chunks(datastore
, Box
::new(index
), verified_chunks
, worker
)
95 fn verify_dynamic_index(
96 datastore
: &DataStore
,
97 backup_dir
: &BackupDir
,
99 verified_chunks
: &mut HashSet
<[u8;32]>,
101 ) -> Result
<(), Error
> {
103 let mut path
= backup_dir
.relative_path();
104 path
.push(&info
.filename
);
106 let index
= datastore
.open_dynamic_reader(&path
)?
;
108 let (csum
, size
) = index
.compute_csum();
109 if size
!= info
.size
{
110 bail
!("wrong size ({} != {})", info
.size
, size
);
113 if csum
!= info
.csum
{
114 bail
!("wrong index checksum");
117 verify_index_chunks(datastore
, Box
::new(index
), verified_chunks
, worker
)
120 /// Verify a single backup snapshot
122 /// This checks all archives inside a backup snapshot.
123 /// Errors are logged to the worker log.
126 /// - Ok(true) if verify is successful
127 /// - Ok(false) if there were verification errors
128 /// - Err(_) if task was aborted
129 pub fn verify_backup_dir(
130 datastore
: &DataStore
,
131 backup_dir
: &BackupDir
,
132 verified_chunks
: &mut HashSet
<[u8;32]>,
134 ) -> Result
<bool
, Error
> {
136 let manifest
= match datastore
.load_manifest(&backup_dir
) {
137 Ok((manifest
, _crypt_mode
, _
)) => manifest
,
139 worker
.log(format
!("verify {}:{} - manifest load error: {}", datastore
.name(), backup_dir
, err
));
144 worker
.log(format
!("verify {}:{}", datastore
.name(), backup_dir
));
146 let mut error_count
= 0;
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
)?
{
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
),
154 ArchiveType
::Blob
=> verify_blob(&datastore
, &backup_dir
, info
),
158 worker
.fail_on_abort()?
;
160 if let Err(err
) = result
{
161 worker
.log(format
!("verify {}:{}/{} failed: {}", datastore
.name(), backup_dir
, info
.filename
, err
));
169 /// Verify all backups inside a backup group
171 /// Errors are logged to the worker log.
174 /// - Ok(true) if verify is successful
175 /// - Ok(false) if there were verification errors
176 /// - Err(_) if task was aborted
177 pub fn verify_backup_group(datastore
: &DataStore
, group
: &BackupGroup
, worker
: &WorkerTask
) -> Result
<bool
, Error
> {
179 let mut list
= match group
.list_backups(&datastore
.base_path()) {
182 worker
.log(format
!("verify group {}:{} - unable to list backups: {}", datastore
.name(), group
, err
));
187 worker
.log(format
!("verify group {}:{}", datastore
.name(), group
));
189 let mut error_count
= 0;
191 let mut verified_chunks
= HashSet
::with_capacity(1024*16); // start with 16384 chunks (up to 65GB)
193 BackupInfo
::sort_list(&mut list
, false); // newest first
195 if !verify_backup_dir(datastore
, &info
.backup_dir
, &mut verified_chunks
, worker
)?
{
203 /// Verify all backups inside a datastore
205 /// Errors are logged to the worker log.
208 /// - Ok(true) if verify is successful
209 /// - Ok(false) if there were verification errors
210 /// - Err(_) if task was aborted
211 pub fn verify_all_backups(datastore
: &DataStore
, worker
: &WorkerTask
) -> Result
<bool
, Error
> {
213 let list
= match BackupGroup
::list_groups(&datastore
.base_path()) {
216 worker
.log(format
!("verify datastore {} - unable to list backups: {}", datastore
.name(), err
));
221 worker
.log(format
!("verify datastore {}", datastore
.name()));
223 let mut error_count
= 0;
225 if !verify_backup_group(datastore
, &group
, worker
)?
{