Rollup merge of #120373 - HTGAzureX1212:HTGAzureX1212/issue-120040, r=ChrisDenton

Adjust Behaviour of `read_dir` and `ReadDir` in Windows Implementation: Check Whether Path to Search In Exists

This pull request changes the `read_dir` function's and the `ReadDir` structure's internal implementations for the Windows operating system to make its behaviour more accurate.

It should be noted that `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileW` function when *no matching files can be found*, not necessarily that the path to search in does not exist in the first place. Therefore, directly returning the "The system cannot find the file specified." may not be accurate.

An extra check for whether the path to search in exists is added, returning a constructed `ReadDir` iterator with its handle being an `INVALID_HANDLE_VALUE` returned by the `FindFirstFileW` function if `ERROR_FILE_NOT_FOUND` is indeed the last OS error. The `ReadDir` implementation for the Windows operating system is correspondingly updated to always return `None` if the handle it has is an `INVALID_HANDLE_VALUE` which can only be the case if and only if specifically constructed by the `read_dir` function in the aforementioned conditions.

It should also be noted that `FindFirstFileW` would have returned `ERROR_PATH_NOT_FOUND` if the path to search in does not exist in the first place.

Presumably fixes #120040.
This commit is contained in:
Dylan DPC 2024-01-29 12:56:52 +00:00 committed by GitHub
commit d04bede047
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -112,6 +112,13 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
if self.handle.0 == c::INVALID_HANDLE_VALUE {
// This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle.
// Simply return `None` because this is only the case when `FindFirstFileW` in
// the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means
// no matchhing files can be found.
return None;
}
if let Some(first) = self.first.take() {
if let Some(e) = DirEntry::new(&self.root, &first) {
return Some(Ok(e));
@ -1068,6 +1075,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
unsafe {
let mut wfd = mem::zeroed();
let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
if find_handle != c::INVALID_HANDLE_VALUE {
Ok(ReadDir {
handle: FindNextFileHandle(find_handle),
@ -1075,7 +1083,31 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
first: Some(wfd),
})
} else {
Err(Error::last_os_error())
// The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileW` function
// if no matching files can be found, but not necessarily that the path to find the
// files in does not exist.
//
// Hence, a check for whether the path to search in exists is added when the last
// os error returned by Windows is `ERROR_FILE_NOT_FOUND` to handle this scenario.
// If that is the case, an empty `ReadDir` iterator is returned as it returns `None`
// in the initial `.next()` invocation because `ERROR_NO_MORE_FILES` would have been
// returned by the `FindNextFileW` function.
//
// See issue #120040: https://github.com/rust-lang/rust/issues/120040.
let last_error = api::get_last_error();
if last_error.code == c::ERROR_FILE_NOT_FOUND {
return Ok(ReadDir {
handle: FindNextFileHandle(find_handle),
root: Arc::new(root),
first: None,
});
}
// Just return the error constructed from the raw OS error if the above is not the case.
//
// Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileW` function
// when the path to search in does not exist in the first place.
Err(Error::from_raw_os_error(last_error.code as i32))
}
}
}