1- use flate2:: write:: GzEncoder ;
21use flate2:: Compression ;
2+ use flate2:: { read:: GzDecoder , write:: GzEncoder } ;
33use mithril_common:: StdResult ;
44use slog_scope:: { info, warn} ;
55use std:: error:: Error as StdError ;
66use std:: fs:: File ;
7- use std:: io;
7+ use std:: io:: { self , Seek , SeekFrom } ;
88use std:: path:: { Path , PathBuf } ;
99use std:: sync:: RwLock ;
10+ use tar:: Archive ;
1011use thiserror:: Error ;
1112
1213use crate :: dependency_injection:: DependenciesBuilderError ;
@@ -53,6 +54,10 @@ pub enum SnapshotError {
5354 #[ error( "Create archive error: {0}" ) ]
5455 CreateArchiveError ( #[ from] io:: Error ) ,
5556
57+ /// Set when the snapshotter creates an invalid snapshot.
58+ #[ error( "Invalid archive error: {0}" ) ]
59+ InvalidArchiveError ( String ) ,
60+
5661 /// Set when the snapshotter fails at uploading the snapshot.
5762 #[ error( "Upload file error: `{0}`" ) ]
5863 UploadFileError ( String ) ,
@@ -65,7 +70,7 @@ pub enum SnapshotError {
6570impl Snapshotter for GzipSnapshotter {
6671 fn snapshot ( & self , archive_name : & str ) -> Result < OngoingSnapshot , SnapshotError > {
6772 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| {
6974 if archive_path. exists ( ) {
7075 if let Err ( remove_error) = std:: fs:: remove_file ( & archive_path) {
7176 warn ! (
@@ -142,6 +147,32 @@ impl GzipSnapshotter {
142147
143148 Ok ( filesize)
144149 }
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+ }
145176}
146177
147178/// Snapshotter that does nothing. It is mainly used for test purposes.
@@ -198,6 +229,8 @@ impl Snapshotter for DumbSnapshotter {
198229mod tests {
199230 use std:: sync:: Arc ;
200231
232+ use mithril_common:: digesters:: DummyImmutablesDbBuilder ;
233+
201234 use super :: * ;
202235
203236 fn get_test_directory ( dir_name : & str ) -> PathBuf {
@@ -289,4 +322,36 @@ mod tests {
289322
290323 assert_eq ! ( vec![ "other-process.file" . to_string( ) ] , remaining_files) ;
291324 }
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+ }
292357}
0 commit comments