From 0fcd5d54552ec758f9e6c6f70071247fb13e117f Mon Sep 17 00:00:00 2001 From: Laurent Bonnans Date: Wed, 26 Feb 2014 17:50:02 +0100 Subject: [PATCH 1/3] fs: use an iterative algorithm for 'mkdir_recursive' as requested in #6109 --- src/libstd/io/fs.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 79e191a9ec90d..3ad35e3124bb8 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -528,10 +528,25 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> { if path.is_dir() { return Ok(()) } - if path.filename().is_some() { - try!(mkdir_recursive(&path.dir_path(), mode)); + + let mut comps = path.components(); + let mut curpath = path.root_path().unwrap_or(Path::new(".")); + + for c in comps { + curpath.push(c); + + match mkdir(&curpath, mode) { + Err(mkdir_err) => { + // already exists ? + if try!(stat(&curpath)).kind != io::TypeDirectory { + return Err(mkdir_err); + } + } + Ok(()) => () + } } - mkdir(path, mode) + + Ok(()) } /// Removes a directory at this path, after removing all its contents. Use From 2d754b49daa897656f6df5aa24b7560dbc35205e Mon Sep 17 00:00:00 2001 From: Laurent Bonnans Date: Wed, 26 Feb 2014 19:16:53 +0100 Subject: [PATCH 2/3] 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. --- src/libstd/io/fs.rs | 49 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 3ad35e3124bb8..330f09ed47a47 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. From 164b7c22b69ed69854c0e579e051f851181673b0 Mon Sep 17 00:00:00 2001 From: Laurent Bonnans Date: Fri, 28 Feb 2014 22:54:07 +0100 Subject: [PATCH 3/3] fs: units tests for mkdir_recusive and rmdir_recursive The rmdir test is blocked by #12795 on windows. --- src/libstd/io/fs.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 330f09ed47a47..3435c9a07aa19 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -968,10 +968,36 @@ mod test { check!(rmdir(dir)); }) + iotest!(fn recursive_mkdir() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1/d2"); + check!(mkdir_recursive(&dir, io::UserRWX)); + assert!(dir.is_dir()) + }) + iotest!(fn recursive_mkdir_slash() { check!(mkdir_recursive(&Path::new("/"), io::UserRWX)); }) + // FIXME(#12795) depends on lstat to work on windows + #[cfg(not(windows))] + iotest!(fn recursive_rmdir() { + let tmpdir = tmpdir(); + let d1 = tmpdir.join("d1"); + let dt = d1.join("t"); + let dtt = dt.join("t"); + let d2 = tmpdir.join("d2"); + let canary = d2.join("do_not_delete"); + check!(mkdir_recursive(&dtt, io::UserRWX)); + check!(mkdir_recursive(&d2, io::UserRWX)); + check!(File::create(&canary).write(bytes!("foo"))); + check!(symlink(&d2, &dt.join("d2"))); + check!(rmdir_recursive(&d1)); + + assert!(!d1.is_dir()); + assert!(canary.exists()); + }) + iotest!(fn unicode_path_is_dir() { assert!(Path::new(".").is_dir()); assert!(!Path::new("test/stdtest/fs.rs").is_dir());