Skip to content

Commit d47036c

Browse files
committed
Don't let remove_dir_all recursively remove a symlink
See #29412
1 parent fb172b6 commit d47036c

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

src/libstd/fs.rs

+34
Original file line numberDiff line numberDiff line change
@@ -1881,12 +1881,46 @@ mod tests {
18811881
check!(fs::create_dir_all(&d2));
18821882
check!(check!(File::create(&canary)).write(b"foo"));
18831883
check!(symlink_junction(&d2, &dt.join("d2")));
1884+
check!(symlink_file(&canary, &d1.join("canary")));
18841885
check!(fs::remove_dir_all(&d1));
18851886

18861887
assert!(!d1.is_dir());
18871888
assert!(canary.exists());
18881889
}
18891890

1891+
#[test]
1892+
fn recursive_rmdir_of_symlink() {
1893+
// test we do not recursively delete a symlink but only dirs.
1894+
let tmpdir = tmpdir();
1895+
let link = tmpdir.join("d1");
1896+
let dir = tmpdir.join("d2");
1897+
let canary = dir.join("do_not_delete");
1898+
check!(fs::create_dir_all(&dir));
1899+
check!(check!(File::create(&canary)).write(b"foo"));
1900+
check!(symlink_junction(&dir, &link));
1901+
check!(fs::remove_dir_all(&link));
1902+
1903+
assert!(!link.is_dir());
1904+
assert!(canary.exists());
1905+
}
1906+
1907+
#[test]
1908+
// only Windows makes a distinction between file and directory symlinks.
1909+
#[cfg(windows)]
1910+
fn recursive_rmdir_of_file_symlink() {
1911+
let tmpdir = tmpdir();
1912+
if !got_symlink_permission(&tmpdir) { return };
1913+
1914+
let f1 = tmpdir.join("f1");
1915+
let f2 = tmpdir.join("f2");
1916+
check!(check!(File::create(&f1)).write(b"foo"));
1917+
check!(symlink_file(&f1, &f2));
1918+
match fs::remove_dir_all(&f2) {
1919+
Ok(..) => panic!("wanted a failure"),
1920+
Err(..) => {}
1921+
}
1922+
}
1923+
18901924
#[test]
18911925
fn unicode_path_is_dir() {
18921926
assert!(Path::new(".").is_dir());

src/libstd/sys/unix/fs.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,19 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
511511
}
512512

513513
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
514+
let filetype = try!(lstat(path)).file_type();
515+
if filetype.is_symlink() {
516+
unlink(path)
517+
} else {
518+
remove_dir_all_recursive(path)
519+
}
520+
}
521+
522+
fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
514523
for child in try!(readdir(path)) {
515524
let child = try!(child);
516525
if try!(child.file_type()).is_dir() {
517-
try!(remove_dir_all(&child.path()));
526+
try!(remove_dir_all_recursive(&child.path()));
518527
} else {
519528
try!(unlink(&child.path()));
520529
}

src/libstd/sys/windows/fs.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -525,11 +525,22 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
525525
}
526526

527527
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
528+
let filetype = try!(lstat(path)).file_type();
529+
if filetype.is_symlink() {
530+
// On Windows symlinks to files and directories are removed differently.
531+
// rmdir only deletes dir symlinks and junctions, not file symlinks.
532+
rmdir(path)
533+
} else {
534+
remove_dir_all_recursive(path)
535+
}
536+
}
537+
538+
fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
528539
for child in try!(readdir(path)) {
529540
let child = try!(child);
530541
let child_type = try!(child.file_type());
531542
if child_type.is_dir() {
532-
try!(remove_dir_all(&child.path()));
543+
try!(remove_dir_all_recursive(&child.path()));
533544
} else if child_type.is_symlink_dir() {
534545
try!(rmdir(&child.path()));
535546
} else {

0 commit comments

Comments
 (0)