fix: fs::remove_dir_all: treat ENOENT as success · patricklam/verify-rust-std@fe33d2c
@@ -2029,6 +2029,7 @@ mod remove_dir_impl {
20292029use crate::path::{Path, PathBuf};
20302030use crate::sys::common::small_c_string::run_path_with_cstr;
20312031use crate::sys::{cvt, cvt_r};
2032+use crate::sys_common::ignore_notfound;
2032203320332034pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
20342035let fd = cvt_r(|| unsafe {
@@ -2082,6 +2083,16 @@ mod remove_dir_impl {
20822083}
20832084}
208420852086+fn is_enoent(result: &io::Result<()>) -> bool {
2087+if let Err(err) = result
2088+&& matches!(err.raw_os_error(), Some(libc::ENOENT))
2089+{
2090+true
2091+} else {
2092+false
2093+}
2094+}
2095+20852096fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
20862097// try opening as directory
20872098let fd = match openat_nofollow_dironly(parent_fd, &path) {
@@ -2105,27 +2116,35 @@ mod remove_dir_impl {
21052116for child in dir {
21062117let child = child?;
21072118let child_name = child.name_cstr();
2108-match is_dir(&child) {
2109-Some(true) => {
2110-remove_dir_all_recursive(Some(fd), child_name)?;
2111-}
2112-Some(false) => {
2113-cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
2114-}
2115-None => {
2116-// POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
2117-// if the process has the appropriate privileges. This however can causing orphaned
2118-// directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
2119-// into it first instead of trying to unlink() it.
2120-remove_dir_all_recursive(Some(fd), child_name)?;
2119+// we need an inner try block, because if one of these
2120+// directories has already been deleted, then we need to
2121+// continue the loop, not return ok.
2122+let result: io::Result<()> = try {
2123+match is_dir(&child) {
2124+Some(true) => {
2125+remove_dir_all_recursive(Some(fd), child_name)?;
2126+}
2127+Some(false) => {
2128+cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
2129+}
2130+None => {
2131+// POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
2132+// if the process has the appropriate privileges. This however can causing orphaned
2133+// directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
2134+// into it first instead of trying to unlink() it.
2135+remove_dir_all_recursive(Some(fd), child_name)?;
2136+}
21212137}
2138+};
2139+if result.is_err() && !is_enoent(&result) {
2140+return result;
21222141}
21232142}
2124214321252144// unlink the directory after removing its contents
2126-cvt(unsafe {
2145+ignore_notfound(cvt(unsafe {
21272146unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR)
2128-})?;
2147+}))?;
21292148Ok(())
21302149}
21312150