@@ -51,6 +51,7 @@ fs::unlink(&path);
5151
5252use c_str:: ToCStr ;
5353use clone:: Clone ;
54+ use container:: Container ;
5455use iter:: Iterator ;
5556use super :: { Reader , Writer , Seek } ;
5657use super :: { SeekStyle , Read , Write , Open , IoError , Truncate ,
@@ -62,6 +63,7 @@ use result::{Ok, Err};
6263use path;
6364use path:: { Path , GenericPath } ;
6465use vec:: { OwnedVector , ImmutableVector } ;
66+ use vec_ng:: Vec ;
6567
6668/// Unconstrained file access type that exposes read and write operations
6769///
@@ -528,10 +530,25 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
528530 if path. is_dir ( ) {
529531 return Ok ( ( ) )
530532 }
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+ }
533549 }
534- mkdir ( path, mode)
550+
551+ Ok ( ( ) )
535552}
536553
537554/// 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<()> {
542559/// This function will return an `Err` value if an error happens. See
543560/// `file::unlink` and `fs::readdir` for possible error conditions.
544561pub 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+ }
551599 }
552600 }
553- // Directory should now be empty
554- rmdir ( path )
601+
602+ Ok ( ( ) )
555603}
556604
557605/// Changes the timestamps for a file's last modification and access time.
@@ -920,10 +968,36 @@ mod test {
920968 check!( rmdir( dir) ) ;
921969 } )
922970
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+
923978 iotest ! ( fn recursive_mkdir_slash( ) {
924979 check!( mkdir_recursive( & Path :: new( "/" ) , io:: UserRWX ) ) ;
925980 } )
926981
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+
9271001 iotest ! ( fn unicode_path_is_dir( ) {
9281002 assert!( Path :: new( "." ) . is_dir( ) ) ;
9291003 assert!( !Path :: new( "test/stdtest/fs.rs" ) . is_dir( ) ) ;
0 commit comments