about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorLaurent Bonnans <bonnans.l@gmail.com>2014-02-26 19:16:53 +0100
committerLaurent Bonnans <bonnans.l@gmail.com>2014-03-10 19:27:59 +0100
commit2d754b49daa897656f6df5aa24b7560dbc35205e (patch)
tree3159bcec9c44c897a53326cf1bee41170503f664 /src/libstd
parent0fcd5d54552ec758f9e6c6f70071247fb13e117f (diff)
downloadrust-2d754b49daa897656f6df5aa24b7560dbc35205e.tar.gz
rust-2d754b49daa897656f6df5aa24b7560dbc35205e.zip
fs: use an iterative algorithm for 'rmdir_recursive'
For now, the windows version uses stat, just as before.
We should switch back to lstat as soon as #12795 is closed.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io/fs.rs49
1 files changed, 41 insertions, 8 deletions
diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs
index 3ad35e3124b..330f09ed47a 100644
--- a/src/libstd/io/fs.rs
+++ b/src/libstd/io/fs.rs
@@ -51,6 +51,7 @@ fs::unlink(&path);
 
 use c_str::ToCStr;
 use clone::Clone;
+use container::Container;
 use iter::Iterator;
 use super::{Reader, Writer, Seek};
 use super::{SeekStyle, Read, Write, Open, IoError, Truncate,
@@ -62,6 +63,7 @@ use result::{Ok, Err};
 use path;
 use path::{Path, GenericPath};
 use vec::{OwnedVector, ImmutableVector};
+use vec_ng::Vec;
 
 /// Unconstrained file access type that exposes read and write operations
 ///
@@ -557,16 +559,47 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
 /// This function will return an `Err` value if an error happens. See
 /// `file::unlink` and `fs::readdir` for possible error conditions.
 pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
-    let children = try!(readdir(path));
-    for child in children.iter() {
-        if child.is_dir() {
-            try!(rmdir_recursive(child));
-        } else {
-            try!(unlink(child));
+    let mut rm_stack = Vec::new();
+    rm_stack.push(path.clone());
+
+    while !rm_stack.is_empty() {
+        let children = try!(readdir(rm_stack.last().unwrap()));
+        let mut has_child_dir = false;
+
+        // delete all regular files in the way and push subdirs
+        // on the stack
+        for child in children.move_iter() {
+            // FIXME(#12795) we should use lstat in all cases
+            let child_type = match cfg!(windows) {
+                true => try!(stat(&child)).kind,
+                false => try!(lstat(&child)).kind
+            };
+
+            if child_type == io::TypeDirectory {
+                rm_stack.push(child);
+                has_child_dir = true;
+            } else {
+                // we can carry on safely if the file is already gone
+                // (eg: deleted by someone else since readdir)
+                match unlink(&child) {
+                    Ok(()) => (),
+                    Err(ref e) if e.kind == io::FileNotFound => (),
+                    Err(e) => return Err(e)
+                }
+            }
+        }
+
+        // if no subdir was found, let's pop and delete
+        if !has_child_dir {
+            match rmdir(&rm_stack.pop().unwrap()) {
+                Ok(()) => (),
+                Err(ref e) if e.kind == io::FileNotFound => (),
+                Err(e) => return Err(e)
+            }
         }
     }
-    // Directory should now be empty
-    rmdir(path)
+
+    Ok(())
 }
 
 /// Changes the timestamps for a file's last modification and access time.