diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 6178f1bbb8e12..45a261befe0aa 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -1069,13 +1069,43 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { } fn _remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = try!(symlink_metadata(path)).file_type(); + if filetype.is_symlink() { + if cfg!(windows) { + // On Windows symlinks to files and directories removed differently + // we should remove only directories here and have error on file + remove_dir(path) + } else { + // On unix symlinks are always files + remove_file(path) + } + } else { + remove_dir_all_recursive(path) + } +} +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { for child in try!(read_dir(path)) { - let child = try!(child).path(); - let stat = try!(symlink_metadata(&*child)); - if stat.is_dir() { - try!(remove_dir_all(&*child)); + let child = try!(child); + let child_path = child.path(); + let child_type = try!(child.file_type()); + if child_type.is_dir() { + try!(remove_dir_all_recursive(&*child_path)); } else { - try!(remove_file(&*child)); + if cfg!(windows) { + if child_type.is_symlink() { + let target_type = try!(metadata(&*child_path)).file_type(); + if target_type.is_dir() { + try!(remove_dir(&*child_path)); + } else { + try!(remove_file(&*child_path)); + } + } else { + try!(remove_file(&*child_path)); + } + } else { + // The FileType::is_dir() is false for symlinks too + try!(remove_file(&*child_path)); + } } } remove_dir(path) @@ -1704,10 +1734,10 @@ mod tests { check!(fs::create_dir_all(&Path2::new("/"))); } - // FIXME(#12795) depends on lstat to work on windows #[cfg(not(windows))] #[test] fn recursive_rmdir() { + use os::unix::fs::symlink; let tmpdir = tmpdir(); let d1 = tmpdir.join("d1"); let dt = d1.join("t"); @@ -1717,13 +1747,83 @@ mod tests { check!(fs::create_dir_all(&dtt)); check!(fs::create_dir_all(&d2)); check!(check!(File::create(&canary)).write(b"foo")); - check!(fs::soft_link(&d2, &dt.join("d2"))); + check!(symlink(&d2, &dt.join("d2"))); check!(fs::remove_dir_all(&d1)); assert!(!d1.is_dir()); assert!(canary.exists()); } + #[cfg(not(windows))] + #[test] + fn recursive_rmdir_of_symlink() { + use os::unix::fs::symlink; + let tmpdir = tmpdir(); + let d1 = tmpdir.join("d1"); + let d2 = tmpdir.join("d2"); + let canary = d2.join("do_not_delete"); + check!(fs::create_dir_all(&d2)); + check!(check!(File::create(&canary)).write(b"foo")); + check!(symlink(&d2, &d1)); + check!(fs::remove_dir_all(&d1)); + + assert!(!d1.is_dir()); + assert!(canary.exists()); + } + + #[cfg(windows)] + #[test] + fn recursive_rmdir() { + use os::windows::fs::{symlink_file, symlink_dir}; + 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!(fs::create_dir_all(&dtt)); + check!(fs::create_dir_all(&d2)); + check!(check!(File::create(&canary)).write(b"foo")); + check!(symlink_dir(&d2, &dt.join("d2"))); + check!(symlink_file(&canary, &d1.join("canary"))); + check!(fs::remove_dir_all(&d1)); + + assert!(!d1.is_dir()); + assert!(canary.exists()); + } + + #[cfg(windows)] + #[test] + fn recursive_rmdir_of_symlink() { + use os::windows::fs::symlink_dir; + let tmpdir = tmpdir(); + let d1 = tmpdir.join("d1"); + let d2 = tmpdir.join("d2"); + let canary = d2.join("do_not_delete"); + check!(fs::create_dir_all(&d2)); + check!(check!(File::create(&canary)).write(b"foo")); + check!(symlink_dir(&d2, &d1)); + check!(fs::remove_dir_all(&d1)); + + assert!(!d1.is_dir()); + assert!(canary.exists()); + } + + #[cfg(windows)] + #[test] + fn recursive_rmdir_of_file_symlink() { + use os::windows::fs::symlink_file; + let tmpdir = tmpdir(); + let f1 = tmpdir.join("f1"); + let f2 = tmpdir.join("f2"); + check!(check!(File::create(&f1)).write(b"foo")); + check!(symlink_file(&f1, &f2)); + match fs::remove_dir_all(&f2) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + } + #[test] fn unicode_path_is_dir() { assert!(Path2::new(".").is_dir());