1212
1313//! Restore from the archive to the filesystem.
1414
15- use std:: fs:: { create_dir_all, File } ;
15+ #[ cfg( test) ]
16+ use std:: collections:: HashMap ;
17+ use std:: fs:: { File , create_dir_all} ;
1618use std:: io:: { self , Write } ;
1719use std:: path:: { Path , PathBuf } ;
1820use std:: sync:: Arc ;
1921
20- use fail:: fail_point;
2122use filetime:: set_file_handle_times;
2223#[ cfg( unix) ]
2324use filetime:: set_symlink_file_times;
@@ -33,17 +34,25 @@ use crate::unix_time::ToFileTime;
3334use crate :: * ;
3435
3536/// Description of how to restore a tree.
36- // #[derive(Debug)]
3737pub struct RestoreOptions {
38+ /// Exclude these paths from being restored.
3839 pub exclude : Exclude ,
40+
3941 /// Restore only this subdirectory.
4042 pub only_subtree : Option < Apath > ,
43+
44+ /// Overwrite existing files in the destination.
4145 pub overwrite : bool ,
42- // The band to select, or by default the last complete one.
46+
47+ /// The band to select, or by default the last complete one.
4348 pub band_selection : BandSelectionPolicy ,
4449
45- // Call this callback as each entry is successfully restored.
50+ /// Call this callback as each entry is successfully restored.
4651 pub change_callback : Option < ChangeCallback > ,
52+
53+ /// For testing, fail to restore the named entries, with the given error.
54+ #[ cfg( test) ]
55+ pub inject_failures : HashMap < Apath , io:: ErrorKind > ,
4756}
4857
4958impl Default for RestoreOptions {
@@ -54,6 +63,8 @@ impl Default for RestoreOptions {
5463 exclude : Exclude :: nothing ( ) ,
5564 only_subtree : None ,
5665 change_callback : None ,
66+ #[ cfg( test) ]
67+ inject_failures : HashMap :: new ( ) ,
5768 }
5869 }
5970}
@@ -98,14 +109,13 @@ pub async fn restore(
98109 Kind :: Dir => {
99110 monitor. count ( Counter :: Dirs , 1 ) ;
100111 if * entry. apath ( ) != Apath :: root ( ) {
101- if let Err ( err) = create_dir ( & path) {
102- if err. kind ( ) != io:: ErrorKind :: AlreadyExists {
103- monitor. error ( Error :: RestoreDirectory {
104- path : path. clone ( ) ,
105- source : err,
106- } ) ;
107- continue ;
108- }
112+ // Create all the parents in case we're restoring only a nested subtree.
113+ if let Err ( err) = restore_dir ( & entry. apath , & path, & options) {
114+ monitor. error ( Error :: RestoreDirectory {
115+ path : path. clone ( ) ,
116+ source : err,
117+ } ) ;
118+ continue ;
109119 }
110120 }
111121 deferrals. push ( DirDeferral {
@@ -146,15 +156,22 @@ pub async fn restore(
146156 Ok ( ( ) )
147157}
148158
149- fn create_dir ( path : & Path ) -> io:: Result < ( ) > {
150- fail_point ! ( "restore::create-dir" , |_| {
151- Err ( io:: Error :: new(
152- io:: ErrorKind :: PermissionDenied ,
153- "Simulated failure" ,
154- ) )
155- } ) ;
156- // Create all the parents in case we're restoring only a nested subtree.
157- create_dir_all ( path)
159+ fn restore_dir ( apath : & Apath , restore_path : & Path , options : & RestoreOptions ) -> io:: Result < ( ) > {
160+ #[ cfg( test) ]
161+ {
162+ if let Some ( err_kind) = options. inject_failures . get ( apath) {
163+ return Err ( io:: Error :: from ( * err_kind) ) ;
164+ }
165+ }
166+ let _ = apath;
167+ let _ = options;
168+ create_dir_all ( restore_path) . or_else ( |err| {
169+ if err. kind ( ) == io:: ErrorKind :: AlreadyExists {
170+ Ok ( ( ) )
171+ } else {
172+ Err ( err)
173+ }
174+ } )
158175}
159176
160177/// Recorded changes to apply to directories after all their contents
@@ -307,13 +324,16 @@ fn restore_symlink(_restore_path: &Path, entry: &IndexEntry) -> Result<()> {
307324#[ cfg( test) ]
308325mod test {
309326 use std:: fs:: { create_dir, write} ;
327+ use std:: io;
328+ use std:: path:: Path ;
310329 use std:: sync:: { Arc , Mutex } ;
311330
312331 use tempfile:: TempDir ;
313332
314333 use crate :: counters:: Counter ;
315334 use crate :: monitor:: test:: TestMonitor ;
316- use crate :: test_fixtures:: { store_two_versions, TreeFixture } ;
335+ use crate :: test_fixtures:: { TreeFixture , store_two_versions} ;
336+ use crate :: transport:: Transport ;
317337 use crate :: * ;
318338
319339 #[ tokio:: test]
@@ -501,7 +521,7 @@ mod test {
501521 use std:: fs:: { read_link, symlink_metadata} ;
502522 use std:: path:: PathBuf ;
503523
504- use filetime:: { set_symlink_file_times , FileTime } ;
524+ use filetime:: { FileTime , set_symlink_file_times } ;
505525
506526 let af = Archive :: create_temp ( ) . await ;
507527 let srcdir = TreeFixture :: new ( ) ;
@@ -530,4 +550,44 @@ mod test {
530550 PathBuf :: from( "target" )
531551 ) ;
532552 }
553+
554+ #[ tokio:: test]
555+ async fn create_dir_permission_denied ( ) {
556+ let archive = Archive :: open ( Transport :: local ( Path :: new (
557+ "testdata/archive/simple/v0.6.10" ,
558+ ) ) )
559+ . await
560+ . unwrap ( ) ;
561+
562+ let mut restore_options = RestoreOptions :: default ( ) ;
563+ restore_options
564+ . inject_failures
565+ . insert ( Apath :: from ( "/subdir" ) , io:: ErrorKind :: PermissionDenied ) ;
566+ let restore_tmp = TempDir :: new ( ) . unwrap ( ) ;
567+ let monitor = TestMonitor :: arc ( ) ;
568+ let stats = restore (
569+ & archive,
570+ restore_tmp. path ( ) ,
571+ restore_options,
572+ monitor. clone ( ) ,
573+ )
574+ . await
575+ . expect ( "Restore" ) ;
576+ dbg ! ( & stats) ;
577+ let errors = monitor. take_errors ( ) ;
578+ dbg ! ( & errors) ;
579+ assert_eq ! ( errors. len( ) , 2 ) ;
580+ if let Error :: RestoreDirectory { path, .. } = & errors[ 0 ] {
581+ assert ! ( path. ends_with( "subdir" ) ) ;
582+ } else {
583+ panic ! ( "Unexpected error {:?}" , errors[ 0 ] ) ;
584+ }
585+ // Also, since we didn't create the directory, we fail to create the file within it.
586+ if let Error :: RestoreFile { path, source } = & errors[ 1 ] {
587+ assert ! ( path. ends_with( "subdir/subfile" ) ) ;
588+ assert_eq ! ( source. kind( ) , io:: ErrorKind :: NotFound ) ;
589+ } else {
590+ panic ! ( "Unexpected error {:?}" , errors[ 1 ] ) ;
591+ }
592+ }
533593}
0 commit comments