@@ -51,7 +51,7 @@ fn assure_is_empty(dir: impl AsRef<Path>) -> std::io::Result<()> {
5151fn submodules_are_instantiated_as_directories ( ) -> crate :: Result {
5252 let mut opts = opts_from_probe ( ) ;
5353 opts. overwrite_existing = false ;
54- let ( _source_tree, destination, _index, _outcome) = checkout_index_in_tmp_dir ( opts. clone ( ) , "make_mixed" ) ?;
54+ let ( _source_tree, destination, _index, _outcome) = checkout_index_in_tmp_dir ( opts. clone ( ) , "make_mixed" , None ) ?;
5555
5656 for path in [ "m1" , "modules/m1" ] {
5757 let sm = destination. path ( ) . join ( path) ;
@@ -68,7 +68,7 @@ fn accidental_writes_through_symlinks_are_prevented_if_overwriting_is_forbidden(
6868 // without overwrite mode, everything is safe.
6969 opts. overwrite_existing = false ;
7070 let ( source_tree, destination, _index, outcome) =
71- checkout_index_in_tmp_dir ( opts. clone ( ) , "make_dangerous_symlink" ) . unwrap ( ) ;
71+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_dangerous_symlink" , None ) . unwrap ( ) ;
7272
7373 let source_files = dir_structure ( & source_tree) ;
7474 let worktree_files = dir_structure ( & destination) ;
@@ -109,7 +109,7 @@ fn writes_through_symlinks_are_prevented_even_if_overwriting_is_allowed() {
109109 // with overwrite mode
110110 opts. overwrite_existing = true ;
111111 let ( source_tree, destination, _index, outcome) =
112- checkout_index_in_tmp_dir ( opts. clone ( ) , "make_dangerous_symlink" ) . unwrap ( ) ;
112+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_dangerous_symlink" , None ) . unwrap ( ) ;
113113
114114 let source_files = dir_structure ( & source_tree) ;
115115 let worktree_files = dir_structure ( & destination) ;
@@ -144,8 +144,13 @@ fn delayed_driver_process() -> crate::Result {
144144 opts. filter_process_delay = gix_filter:: driver:: apply:: Delay :: Allow ;
145145 opts. destination_is_initially_empty = false ;
146146 setup_filter_pipeline ( opts. filters . options_mut ( ) ) ;
147- let ( _source, destination, _index, outcome) =
148- checkout_index_in_tmp_dir_opts ( opts, "make_mixed_without_submodules_and_symlinks" , |_| true , |_| Ok ( ( ) ) ) ?;
147+ let ( _source, destination, _index, outcome) = checkout_index_in_tmp_dir_opts (
148+ opts,
149+ "make_mixed_without_submodules_and_symlinks" ,
150+ None ,
151+ |_| true ,
152+ |_| Ok ( ( ) ) ,
153+ ) ?;
149154 assert_eq ! ( outcome. collisions. len( ) , 0 ) ;
150155 assert_eq ! ( outcome. errors. len( ) , 0 ) ;
151156 assert_eq ! ( outcome. files_updated, 5 ) ;
@@ -189,6 +194,7 @@ fn overwriting_files_and_lone_directories_works() -> crate::Result {
189194 let ( source, destination, _index, outcome) = checkout_index_in_tmp_dir_opts (
190195 opts. clone ( ) ,
191196 "make_mixed" ,
197+ None ,
192198 |_| true ,
193199 |d| {
194200 let empty = d. join ( "empty" ) ;
@@ -254,7 +260,7 @@ fn symlinks_become_files_if_disabled() -> crate::Result {
254260 let mut opts = opts_from_probe ( ) ;
255261 opts. fs . symlink = false ;
256262 let ( source_tree, destination, _index, outcome) =
257- checkout_index_in_tmp_dir ( opts. clone ( ) , "make_mixed_without_submodules" ) ?;
263+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_mixed_without_submodules" , None ) ?;
258264
259265 assert_equality ( & source_tree, & destination, opts. fs . symlink ) ?;
260266 assert ! ( outcome. collisions. is_empty( ) ) ;
@@ -270,7 +276,7 @@ fn dangling_symlinks_can_be_created() -> crate::Result {
270276 }
271277
272278 let ( _source_tree, destination, _index, outcome) =
273- checkout_index_in_tmp_dir ( opts. clone ( ) , "make_dangling_symlink" ) ?;
279+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_dangling_symlink" , None ) ?;
274280 let worktree_files = dir_structure ( & destination) ;
275281 let worktree_files_stripped = stripped_prefix ( & destination, & worktree_files) ;
276282
@@ -291,7 +297,7 @@ fn allow_or_disallow_symlinks() -> crate::Result {
291297 for allowed in & [ false , true ] {
292298 opts. fs . symlink = * allowed;
293299 let ( source_tree, destination, _index, outcome) =
294- checkout_index_in_tmp_dir ( opts. clone ( ) , "make_mixed_without_submodules" ) ?;
300+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_mixed_without_submodules" , None ) ?;
295301
296302 assert_equality ( & source_tree, & destination, opts. fs . symlink ) ?;
297303 assert ! ( outcome. collisions. is_empty( ) ) ;
@@ -307,6 +313,7 @@ fn keep_going_collects_results() {
307313 let ( _source_tree, destination, _index, outcome) = checkout_index_in_tmp_dir_opts (
308314 opts,
309315 "make_mixed_without_submodules" ,
316+ None ,
310317 |_id| {
311318 count
312319 . fetch_update ( Ordering :: SeqCst , Ordering :: SeqCst , |current| {
@@ -369,7 +376,7 @@ fn no_case_related_collisions_on_case_sensitive_filesystem() {
369376 return ;
370377 }
371378 let ( source_tree, destination, index, outcome) =
372- checkout_index_in_tmp_dir ( opts. clone ( ) , "make_ignorecase_collisions" ) . unwrap ( ) ;
379+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_ignorecase_collisions" , None ) . unwrap ( ) ;
373380
374381 assert ! ( outcome. collisions. is_empty( ) ) ;
375382 let num_files = assert_equality ( & source_tree, & destination, opts. fs . symlink ) . unwrap ( ) ;
@@ -384,6 +391,48 @@ fn no_case_related_collisions_on_case_sensitive_filesystem() {
384391 ) ;
385392}
386393
394+ #[ test]
395+ fn safety_checks_dotdot_trees ( ) {
396+ let mut opts = opts_from_probe ( ) ;
397+ let err =
398+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_traverse_trees" , Some ( "traverse_dotdot_trees" ) ) . unwrap_err ( ) ;
399+ let expected_err_msg = "Input path \" ../outside\" contains relative or absolute components" ;
400+ assert_eq ! ( err. source( ) . expect( "inner" ) . to_string( ) , expected_err_msg) ;
401+
402+ opts. keep_going = true ;
403+ let ( _source_tree, _destination, _index, outcome) =
404+ checkout_index_in_tmp_dir ( opts, "make_traverse_trees" , Some ( "traverse_dotdot_trees" ) )
405+ . expect ( "keep-going checks out as much as possible" ) ;
406+ assert_eq ! ( outcome. errors. len( ) , 1 , "one path could not be checked out" ) ;
407+ assert_eq ! (
408+ outcome. errors[ 0 ] . error. source( ) . expect( "inner" ) . to_string( ) ,
409+ expected_err_msg
410+ ) ;
411+ }
412+
413+ #[ test]
414+ fn safety_checks_dotgit_trees ( ) {
415+ let opts = opts_from_probe ( ) ;
416+ let err =
417+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_traverse_trees" , Some ( "traverse_dotgit_trees" ) ) . unwrap_err ( ) ;
418+ assert_eq ! (
419+ err. source( ) . expect( "inner" ) . to_string( ) ,
420+ "The .git name may never be used"
421+ ) ;
422+ }
423+
424+ #[ test]
425+ fn safety_checks_dotgit_ntfs_stream ( ) {
426+ let opts = opts_from_probe ( ) ;
427+ let err =
428+ checkout_index_in_tmp_dir ( opts. clone ( ) , "make_traverse_trees" , Some ( "traverse_dotgit_stream" ) ) . unwrap_err ( ) ;
429+ assert_eq ! (
430+ err. source( ) . expect( "inner" ) . to_string( ) ,
431+ "The .git name may never be used" ,
432+ "note how it is still discovered even though the path is `.git::$INDEX_ALLOCATION`"
433+ ) ;
434+ }
435+
387436#[ test]
388437fn collisions_are_detected_on_a_case_insensitive_filesystem_even_with_delayed_filters ( ) {
389438 let mut opts = opts_from_probe ( ) ;
@@ -394,7 +443,7 @@ fn collisions_are_detected_on_a_case_insensitive_filesystem_even_with_delayed_fi
394443 setup_filter_pipeline ( opts. filters . options_mut ( ) ) ;
395444 opts. filter_process_delay = gix_filter:: driver:: apply:: Delay :: Allow ;
396445 let ( source_tree, destination, _index, outcome) =
397- checkout_index_in_tmp_dir ( opts, "make_ignorecase_collisions" ) . unwrap ( ) ;
446+ checkout_index_in_tmp_dir ( opts, "make_ignorecase_collisions" , None ) . unwrap ( ) ;
398447
399448 let source_files = dir_structure ( & source_tree) ;
400449 assert_eq ! (
@@ -506,17 +555,26 @@ pub fn dir_structure<P: AsRef<std::path::Path>>(path: P) -> Vec<std::path::PathB
506555fn checkout_index_in_tmp_dir (
507556 opts : gix_worktree_state:: checkout:: Options ,
508557 name : & str ,
558+ subdir_name : Option < & str > ,
509559) -> crate :: Result < ( PathBuf , TempDir , gix_index:: File , gix_worktree_state:: checkout:: Outcome ) > {
510- checkout_index_in_tmp_dir_opts ( opts, name, |_d| true , |_| Ok ( ( ) ) )
560+ checkout_index_in_tmp_dir_opts ( opts, name, subdir_name , |_d| true , |_| Ok ( ( ) ) )
511561}
512562
513563fn checkout_index_in_tmp_dir_opts (
514564 opts : gix_worktree_state:: checkout:: Options ,
515- name : & str ,
565+ script_name : & str ,
566+ subdir_name : Option < & str > ,
516567 allow_return_object : impl FnMut ( & gix_hash:: oid ) -> bool + Send + Clone ,
517568 prep_dest : impl Fn ( & Path ) -> std:: io:: Result < ( ) > ,
518569) -> crate :: Result < ( PathBuf , TempDir , gix_index:: File , gix_worktree_state:: checkout:: Outcome ) > {
519- let source_tree = fixture_path ( name) ;
570+ let source_tree = {
571+ let root = fixture_path ( script_name) ;
572+ if let Some ( name) = subdir_name {
573+ root. join ( name)
574+ } else {
575+ root
576+ }
577+ } ;
520578 let git_dir = source_tree. join ( ".git" ) ;
521579 let mut index = gix_index:: File :: at ( git_dir. join ( "index" ) , gix_hash:: Kind :: Sha1 , false , Default :: default ( ) ) ?;
522580 let odb = gix_odb:: at ( git_dir. join ( "objects" ) ) ?. into_inner ( ) . into_arc ( ) ?;
0 commit comments