fix: fs::remove_dir_all: treat ENOENT as success · patricklam/verify-rust-std@fe33d2c

@@ -2029,6 +2029,7 @@ mod remove_dir_impl {

20292029

use crate::path::{Path, PathBuf};

20302030

use crate::sys::common::small_c_string::run_path_with_cstr;

20312031

use crate::sys::{cvt, cvt_r};

2032+

use crate::sys_common::ignore_notfound;

2032203320332034

pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {

20342035

let 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+20852096

fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {

20862097

// try opening as directory

20872098

let fd = match openat_nofollow_dironly(parent_fd, &path) {

@@ -2105,27 +2116,35 @@ mod remove_dir_impl {

21052116

for child in dir {

21062117

let child = child?;

21072118

let 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 {

21272146

unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR)

2128-

})?;

2147+

}))?;

21292148

Ok(())

21302149

}

21312150