@@ -51,6 +51,7 @@ fs::unlink(&path);
51
51
52
52
use c_str:: ToCStr ;
53
53
use clone:: Clone ;
54
+ use container:: Container ;
54
55
use iter:: Iterator ;
55
56
use super :: { Reader , Writer , Seek } ;
56
57
use super :: { SeekStyle , Read , Write , Open , IoError , Truncate ,
@@ -62,6 +63,7 @@ use result::{Ok, Err};
62
63
use path;
63
64
use path:: { Path , GenericPath } ;
64
65
use vec:: { OwnedVector , ImmutableVector } ;
66
+ use vec_ng:: Vec ;
65
67
66
68
/// Unconstrained file access type that exposes read and write operations
67
69
///
@@ -528,10 +530,25 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
528
530
if path. is_dir ( ) {
529
531
return Ok ( ( ) )
530
532
}
531
- if path. filename ( ) . is_some ( ) {
532
- try!( mkdir_recursive ( & path. dir_path ( ) , mode) ) ;
533
+
534
+ let mut comps = path. components ( ) ;
535
+ let mut curpath = path. root_path ( ) . unwrap_or ( Path :: new ( "." ) ) ;
536
+
537
+ for c in comps {
538
+ curpath. push ( c) ;
539
+
540
+ match mkdir ( & curpath, mode) {
541
+ Err ( mkdir_err) => {
542
+ // already exists ?
543
+ if try!( stat ( & curpath) ) . kind != io:: TypeDirectory {
544
+ return Err ( mkdir_err) ;
545
+ }
546
+ }
547
+ Ok ( ( ) ) => ( )
548
+ }
533
549
}
534
- mkdir ( path, mode)
550
+
551
+ Ok ( ( ) )
535
552
}
536
553
537
554
/// Removes a directory at this path, after removing all its contents. Use
@@ -542,16 +559,47 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
542
559
/// This function will return an `Err` value if an error happens. See
543
560
/// `file::unlink` and `fs::readdir` for possible error conditions.
544
561
pub fn rmdir_recursive ( path : & Path ) -> IoResult < ( ) > {
545
- let children = try!( readdir ( path) ) ;
546
- for child in children. iter ( ) {
547
- if child. is_dir ( ) {
548
- try!( rmdir_recursive ( child) ) ;
549
- } else {
550
- try!( unlink ( child) ) ;
562
+ let mut rm_stack = Vec :: new ( ) ;
563
+ rm_stack. push ( path. clone ( ) ) ;
564
+
565
+ while !rm_stack. is_empty ( ) {
566
+ let children = try!( readdir ( rm_stack. last ( ) . unwrap ( ) ) ) ;
567
+ let mut has_child_dir = false ;
568
+
569
+ // delete all regular files in the way and push subdirs
570
+ // on the stack
571
+ for child in children. move_iter ( ) {
572
+ // FIXME(#12795) we should use lstat in all cases
573
+ let child_type = match cfg ! ( windows) {
574
+ true => try!( stat ( & child) ) . kind ,
575
+ false => try!( lstat ( & child) ) . kind
576
+ } ;
577
+
578
+ if child_type == io:: TypeDirectory {
579
+ rm_stack. push ( child) ;
580
+ has_child_dir = true ;
581
+ } else {
582
+ // we can carry on safely if the file is already gone
583
+ // (eg: deleted by someone else since readdir)
584
+ match unlink ( & child) {
585
+ Ok ( ( ) ) => ( ) ,
586
+ Err ( ref e) if e. kind == io:: FileNotFound => ( ) ,
587
+ Err ( e) => return Err ( e)
588
+ }
589
+ }
590
+ }
591
+
592
+ // if no subdir was found, let's pop and delete
593
+ if !has_child_dir {
594
+ match rmdir ( & rm_stack. pop ( ) . unwrap ( ) ) {
595
+ Ok ( ( ) ) => ( ) ,
596
+ Err ( ref e) if e. kind == io:: FileNotFound => ( ) ,
597
+ Err ( e) => return Err ( e)
598
+ }
551
599
}
552
600
}
553
- // Directory should now be empty
554
- rmdir ( path )
601
+
602
+ Ok ( ( ) )
555
603
}
556
604
557
605
/// Changes the timestamps for a file's last modification and access time.
@@ -920,10 +968,36 @@ mod test {
920
968
check!( rmdir( dir) ) ;
921
969
} )
922
970
971
+ iotest ! ( fn recursive_mkdir( ) {
972
+ let tmpdir = tmpdir( ) ;
973
+ let dir = tmpdir. join( "d1/d2" ) ;
974
+ check!( mkdir_recursive( & dir, io:: UserRWX ) ) ;
975
+ assert!( dir. is_dir( ) )
976
+ } )
977
+
923
978
iotest ! ( fn recursive_mkdir_slash( ) {
924
979
check!( mkdir_recursive( & Path :: new( "/" ) , io:: UserRWX ) ) ;
925
980
} )
926
981
982
+ // FIXME(#12795) depends on lstat to work on windows
983
+ #[ cfg( not( windows) ) ]
984
+ iotest ! ( fn recursive_rmdir( ) {
985
+ let tmpdir = tmpdir( ) ;
986
+ let d1 = tmpdir. join( "d1" ) ;
987
+ let dt = d1. join( "t" ) ;
988
+ let dtt = dt. join( "t" ) ;
989
+ let d2 = tmpdir. join( "d2" ) ;
990
+ let canary = d2. join( "do_not_delete" ) ;
991
+ check!( mkdir_recursive( & dtt, io:: UserRWX ) ) ;
992
+ check!( mkdir_recursive( & d2, io:: UserRWX ) ) ;
993
+ check!( File :: create( & canary) . write( bytes!( "foo" ) ) ) ;
994
+ check!( symlink( & d2, & dt. join( "d2" ) ) ) ;
995
+ check!( rmdir_recursive( & d1) ) ;
996
+
997
+ assert!( !d1. is_dir( ) ) ;
998
+ assert!( canary. exists( ) ) ;
999
+ } )
1000
+
927
1001
iotest ! ( fn unicode_path_is_dir( ) {
928
1002
assert!( Path :: new( "." ) . is_dir( ) ) ;
929
1003
assert!( !Path :: new( "test/stdtest/fs.rs" ) . is_dir( ) ) ;
0 commit comments