about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorPaul Dicker <pitdicker@gmail.com>2016-02-07 19:31:14 +0100
committerPaul Dicker <pitdicker@gmail.com>2016-02-07 19:31:14 +0100
commitd47036cbd1e181da6eca7f4f125e092bedeb35e0 (patch)
tree7303638170fdc3a3d75466037c2914f01ad5bc17 /src/libstd
parentfb172b676e5ab951e58b98cede795ab1a7557a58 (diff)
downloadrust-d47036cbd1e181da6eca7f4f125e092bedeb35e0.tar.gz
rust-d47036cbd1e181da6eca7f4f125e092bedeb35e0.zip
Don't let `remove_dir_all` recursively remove a symlink
See #29412
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/fs.rs34
-rw-r--r--src/libstd/sys/unix/fs.rs11
-rw-r--r--src/libstd/sys/windows/fs.rs13
3 files changed, 56 insertions, 2 deletions
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index d1f0fe517e2..e7707d29f7f 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -1881,6 +1881,7 @@ mod tests {
         check!(fs::create_dir_all(&d2));
         check!(check!(File::create(&canary)).write(b"foo"));
         check!(symlink_junction(&d2, &dt.join("d2")));
+        check!(symlink_file(&canary, &d1.join("canary")));
         check!(fs::remove_dir_all(&d1));
 
         assert!(!d1.is_dir());
@@ -1888,6 +1889,39 @@ mod tests {
     }
 
     #[test]
+    fn recursive_rmdir_of_symlink() {
+        // test we do not recursively delete a symlink but only dirs.
+        let tmpdir = tmpdir();
+        let link = tmpdir.join("d1");
+        let dir = tmpdir.join("d2");
+        let canary = dir.join("do_not_delete");
+        check!(fs::create_dir_all(&dir));
+        check!(check!(File::create(&canary)).write(b"foo"));
+        check!(symlink_junction(&dir, &link));
+        check!(fs::remove_dir_all(&link));
+
+        assert!(!link.is_dir());
+        assert!(canary.exists());
+    }
+
+    #[test]
+    // only Windows makes a distinction between file and directory symlinks.
+    #[cfg(windows)]
+    fn recursive_rmdir_of_file_symlink() {
+        let tmpdir = tmpdir();
+        if !got_symlink_permission(&tmpdir) { return };
+
+        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!(Path::new(".").is_dir());
         assert!(!Path::new("test/stdtest/fs.rs").is_dir());
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index a2e6f467b17..b9385ebe5c5 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -511,10 +511,19 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
 }
 
 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    let filetype = try!(lstat(path)).file_type();
+    if filetype.is_symlink() {
+        unlink(path)
+    } else {
+        remove_dir_all_recursive(path)
+    }
+}
+
+fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
     for child in try!(readdir(path)) {
         let child = try!(child);
         if try!(child.file_type()).is_dir() {
-            try!(remove_dir_all(&child.path()));
+            try!(remove_dir_all_recursive(&child.path()));
         } else {
             try!(unlink(&child.path()));
         }
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index 9f9290b751d..b0decfa463f 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -525,11 +525,22 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
 }
 
 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    let filetype = try!(lstat(path)).file_type();
+    if filetype.is_symlink() {
+        // On Windows symlinks to files and directories are removed differently.
+        // rmdir only deletes dir symlinks and junctions, not file symlinks.
+        rmdir(path)
+    } else {
+        remove_dir_all_recursive(path)
+    }
+}
+
+fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
     for child in try!(readdir(path)) {
         let child = try!(child);
         let child_type = try!(child.file_type());
         if child_type.is_dir() {
-            try!(remove_dir_all(&child.path()));
+            try!(remove_dir_all_recursive(&child.path()));
         } else if child_type.is_symlink_dir() {
             try!(rmdir(&child.path()));
         } else {