1
- use flate2:: write:: GzEncoder ;
2
1
use flate2:: Compression ;
2
+ use flate2:: { read:: GzDecoder , write:: GzEncoder } ;
3
3
use mithril_common:: StdResult ;
4
4
use slog_scope:: { info, warn} ;
5
5
use std:: error:: Error as StdError ;
6
6
use std:: fs:: File ;
7
- use std:: io;
7
+ use std:: io:: { self , Seek , SeekFrom } ;
8
8
use std:: path:: { Path , PathBuf } ;
9
9
use std:: sync:: RwLock ;
10
+ use tar:: Archive ;
10
11
use thiserror:: Error ;
11
12
12
13
use crate :: dependency_injection:: DependenciesBuilderError ;
@@ -53,6 +54,10 @@ pub enum SnapshotError {
53
54
#[ error( "Create archive error: {0}" ) ]
54
55
CreateArchiveError ( #[ from] io:: Error ) ,
55
56
57
+ /// Set when the snapshotter creates an invalid snapshot.
58
+ #[ error( "Invalid archive error: {0}" ) ]
59
+ InvalidArchiveError ( String ) ,
60
+
56
61
/// Set when the snapshotter fails at uploading the snapshot.
57
62
#[ error( "Upload file error: `{0}`" ) ]
58
63
UploadFileError ( String ) ,
@@ -65,7 +70,7 @@ pub enum SnapshotError {
65
70
impl Snapshotter for GzipSnapshotter {
66
71
fn snapshot ( & self , archive_name : & str ) -> Result < OngoingSnapshot , SnapshotError > {
67
72
let archive_path = self . ongoing_snapshot_directory . join ( archive_name) ;
68
- let filesize = self . create_archive ( & archive_path) . map_err ( |err| {
73
+ let filesize = self . create_and_verify_archive ( & archive_path) . map_err ( |err| {
69
74
if archive_path. exists ( ) {
70
75
if let Err ( remove_error) = std:: fs:: remove_file ( & archive_path) {
71
76
warn ! (
@@ -142,6 +147,32 @@ impl GzipSnapshotter {
142
147
143
148
Ok ( filesize)
144
149
}
150
+
151
+ fn create_and_verify_archive ( & self , archive_path : & Path ) -> Result < u64 , SnapshotError > {
152
+ let filesize = self . create_archive ( archive_path) ?;
153
+ self . verify_archive ( archive_path) ?;
154
+
155
+ Ok ( filesize)
156
+ }
157
+
158
+ // Verify if an archive is corrupted (i.e. at least one entry is invalid)
159
+ fn verify_archive ( & self , archive_path : & Path ) -> Result < ( ) , SnapshotError > {
160
+ info ! ( "verifying archive: {}" , archive_path. display( ) ) ;
161
+
162
+ let mut snapshot_file_tar_gz = File :: open ( archive_path)
163
+ . map_err ( |e| SnapshotError :: InvalidArchiveError ( e. to_string ( ) ) ) ?;
164
+
165
+ snapshot_file_tar_gz. seek ( SeekFrom :: Start ( 0 ) ) ?;
166
+ let snapshot_file_tar = GzDecoder :: new ( snapshot_file_tar_gz) ;
167
+ let mut snapshot_archive = Archive :: new ( snapshot_file_tar) ;
168
+
169
+ match snapshot_archive. entries ( ) ?. find ( |e| e. is_err ( ) ) {
170
+ Some ( Err ( e) ) => Err ( SnapshotError :: InvalidArchiveError ( format ! (
171
+ "invalid entry with error: '{e:?}'"
172
+ ) ) ) ,
173
+ _ => Ok ( ( ) ) ,
174
+ }
175
+ }
145
176
}
146
177
147
178
/// Snapshotter that does nothing. It is mainly used for test purposes.
@@ -198,6 +229,8 @@ impl Snapshotter for DumbSnapshotter {
198
229
mod tests {
199
230
use std:: sync:: Arc ;
200
231
232
+ use mithril_common:: digesters:: DummyImmutablesDbBuilder ;
233
+
201
234
use super :: * ;
202
235
203
236
fn get_test_directory ( dir_name : & str ) -> PathBuf {
@@ -289,4 +322,36 @@ mod tests {
289
322
290
323
assert_eq ! ( vec![ "other-process.file" . to_string( ) ] , remaining_files) ;
291
324
}
325
+
326
+ #[ test]
327
+ fn should_create_a_valid_archive_with_gzip_snapshotter ( ) {
328
+ let test_dir = get_test_directory ( "should_create_a_valid_archive_with_gzip_snapshotter" ) ;
329
+ let pending_snapshot_directory = test_dir. join ( "pending_snapshot" ) ;
330
+ let pending_snapshot_archive_file = "archive.tar.gz" ;
331
+ let db_directory = test_dir. join ( "db" ) ;
332
+
333
+ DummyImmutablesDbBuilder :: new ( db_directory. as_os_str ( ) . to_str ( ) . unwrap ( ) )
334
+ . with_immutables ( & [ 1 , 2 , 3 ] )
335
+ . append_immutable_trio ( )
336
+ . build ( ) ;
337
+
338
+ let snapshotter = Arc :: new (
339
+ GzipSnapshotter :: new ( db_directory, pending_snapshot_directory. clone ( ) ) . unwrap ( ) ,
340
+ ) ;
341
+
342
+ snapshotter
343
+ . create_archive (
344
+ & pending_snapshot_directory. join ( Path :: new ( pending_snapshot_archive_file) ) ,
345
+ )
346
+ . expect ( "create_archive should not fail" ) ;
347
+ snapshotter
348
+ . verify_archive (
349
+ & pending_snapshot_directory. join ( Path :: new ( pending_snapshot_archive_file) ) ,
350
+ )
351
+ . expect ( "verify_archive should not fail" ) ;
352
+
353
+ snapshotter
354
+ . snapshot ( pending_snapshot_archive_file)
355
+ . expect ( "Snapshotter::snapshot should not fail." ) ;
356
+ }
292
357
}
0 commit comments