Skip to content

re-implement Windows ReadDir on top of GetFileInformationByHandleEx - #164529

Open
RalfJung wants to merge 2 commits into
rust-lang:mainfrom
RalfJung:windows-read-dir
Open

RalfJung wants to merge 2 commits into
rust-lang:mainfrom
RalfJung:windows-read-dir

Conversation

@RalfJung

@RalfJung RalfJung commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

View all comments

The current implementation based on FindFirstFileExW fundamentally relies on knowing the path to the directory, so implementing Dir::read_dir (iterating a directory based on a Dir handle) is not possible. remove_dir_all already uses a completely different implementation based on GetFileInformationByHandleEx that can work with a directory handle. This PR adjusts fs::read_dir to also use an implementation based on GetFileInformationByHandleEx.

The directory entry buffer is currently 1 KiB large, as that's what we are using for remove_dir_all. That is fairly small; it means we have to do a new syscall around every 9 entries. For comparison, the glibc implementation uses a buffer of 32 KiB by default.

try-job: test-x86_64-msvc*

@rustbot rustbot added O-windows Operating system: Windows S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Oct 18, 2026
Comment on lines 1064 to 1065
impl From<c::WIN32_FIND_DATAW> for FileAttr {
fn from(wfd: c::WIN32_FIND_DATAW) -> Self {

@RalfJung RalfJung Oct 18, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this conversion from WIN32_FIND_DATAW is still used by fs::metadata. File::metadata uses yet another conversion, from BY_HANDLE_FILE_INFORMATION. And DirEntry::metadata now uses FILE_ID_BOTH_DIR_INFO.

No idea if it makes sense to consolidate that, but I'd say that would be a job for a different PR.

View changes since the review

@RalfJung

Copy link
Copy Markdown
Member Author

I don't have a Windows machine so this is only build-tested so far...
@bors try

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Oct 18, 2026
re-implement Windows ReadDir on top of GetFileInformationByHandleEx

try-job: test-x86_64-msvc*
@rust-log-analyzer

This comment has been minimized.

@rust-bors

This comment was marked as resolved.

@RalfJung

Copy link
Copy Markdown
Member Author

@bors try

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Oct 18, 2026
re-implement Windows ReadDir on top of GetFileInformationByHandleEx

try-job: test-x86_64-msvc*
@rust-bors

rust-bors Bot commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: c9ad66c (c9ad66c5d99336ce105e9b2591e1c8f87d9ecf22)
Base parent: 15c1044 (15c10447a46529b68e38c7f138aeb95240e4d7d6)

@RalfJung

Copy link
Copy Markdown
Member Author

@bors try

rust-bors Bot pushed a commit that referenced this pull request Oct 19, 2026
re-implement Windows ReadDir on top of GetFileInformationByHandleEx

try-job: test-x86_64-msvc*
@rust-bors

This comment has been minimized.

// `read_dir` on a non-directory should fail, but Windows `Dir::open` suceeds.
// So we have to specifically check for this.
return Err(io::const_error!(io::ErrorKind::NotADirectory, "path is not a directory"));
}

@RalfJung RalfJung Oct 19, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it maybe be better to call buf.fill() already here? Would that "naturall" throw an error for non-directories, or would we still need a dedicated check then?
Currently we delay that until the first next() call. That matches what glibc does.

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you currently have is fine for now. Ideally we would be using Dir to limit it to opening a directory but, as I said, I'm uncomfortable tying this to a new unstable feature.

@RalfJung RalfJung Oct 20, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Dir::open (open dir with just a path) will happily open files on Windows, so using Dir would not help.

Dir::open_dir (open dir relative to Dir handle) ensures that it has a directory, but that requires us to already have a Dir to begin with.

(Yeah I find that naming quite confusing... but it's what got approved in the ACP so I guess other people like it.)

@RalfJung
RalfJung marked this pull request as ready for review October 19, 2026 06:09
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 19, 2026
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Oct 19, 2026
@rustbot

rustbot commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator

r? @Mark-Simulacrum

rustbot has assigned @Mark-Simulacrum.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @ChrisDenton, libs
  • @ChrisDenton, libs expanded to 13 candidates
  • Random selection from JohnTitor, Mark-Simulacrum

@RalfJung

Copy link
Copy Markdown
Member Author

r? @ChrisDenton

@rustbot

rustbot commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator

ChrisDenton is currently at their maximum review capacity.
They may take a while to respond.

@rust-log-analyzer

This comment has been minimized.

@rust-bors

rust-bors Bot commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 60c0f2e (60c0f2e84cba1dcbd8c1f6b32d9cfae5fc6af11b)
Base parent: b6eeb93 (b6eeb93c992027a03e1fd120b6652546396b142c)

@ChrisDenton

Copy link
Copy Markdown
Member

While I think this is the right long-term direction I admit I am concerned about re-implementing an important and long-stable function on top of an experimental and only partially implemented new feature. This would make std::fs::read_dir sensitive to any changes or problems with Dir.

@RalfJung

Copy link
Copy Markdown
Member Author

Note that the only thing we do with Dir is Dir::open, which is a fairly "traditional" method (it uses CreateFileW, not NtCreateFile). I can inline that CreateFileW call if you prefer.

This is on the critical path for rust-lang/miri#4982 so I'd like to find some way to make progress here. Happy to take suggestions for the best way to do that.

@RalfJung

Copy link
Copy Markdown
Member Author

Also, CreateFileW + GetFileInformationByHandleEx has been used in remove_dir_all on Windows for a long time now. So I'd say that combination is pretty battle-tested at this point.

Rather than using Dir::open I can also use File::open to get the handle. That's what remove_dir_all does. It just seemed kind of hacky.

@RalfJung

Copy link
Copy Markdown
Member Author

I have reused the code from remove_dir_all now for opening the directory, so this no longer has any interaction with fs::Dir.

@bors try

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Oct 20, 2026
re-implement Windows ReadDir on top of GetFileInformationByHandleEx

try-job: test-x86_64-msvc*
@rust-bors

rust-bors Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 54d7313 (54d7313ab1a96805f76681b87b2d85369a5652f3)
Base parent: 39b126b (39b126b8e227197c688fed9f9962658bdd89a761)

fs::create_dir_all(&tmpdir.join("a")).unwrap();
File::create(&tmpdir.join("b")).unwrap();
num_files += 2;
if got_symlink_permission(&tmpdir) {

@ChrisDenton ChrisDenton Oct 20, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This either shouldn't use the same tmpdir as is used for the read_dir tests or else got_symlink_permission should cleanup any files it creates.

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, good point. I made got_symlink_permission clean up after itself.

Comment thread library/std/src/sys/fs/windows.rs Outdated
@RalfJung
RalfJung force-pushed the windows-read-dir branch 3 times, most recently from 97c18f3 to 9de1464 Compare October 21, 2026 10:23
@rustbot

This comment has been minimized.

@RalfJung

Copy link
Copy Markdown
Member Author

@bors try

rust-bors Bot pushed a commit that referenced this pull request Oct 21, 2026
re-implement Windows ReadDir on top of GetFileInformationByHandleEx

try-job: test-x86_64-msvc*
@rust-bors

This comment has been minimized.

options.read(true);
// FIXME: use race-free handle-based approach instead.
Dir::open(&self.root, &options)
dir_handle.open_dir(Path::new(""), &options)

@RalfJung RalfJung Oct 21, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have an underlying handle, we can also implement this method properly. :)

(This is only used by the unstable ReadDir::dir.)

View changes since the review

@rust-bors

rust-bors Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 7369f4e (7369f4e2cf33f04741ad8ca17a291ead9e7b1bba)
Base parent: 1731fae (1731faea0da5b4ebf881d8e53f98066c17a74711)

@rust-bors

This comment has been minimized.

@rustbot

rustbot commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

O-windows Operating system: Windows S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants