rust-lang / rust-lang/libs-team
Add an equivalent `DirBuilderExt` trait on Windows + introducing creating subdirectories from `Dir` with certain permissions upfront
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 178
- Forks
- 28
- Avg merge
- 15m
- Merged PRs (30d)
- 1
Description
Proposal
Problem statement
Currently, on Unix, we have a DirBuilderExt trait that contains a single method: DirBuilderExt::mode. What this does is it allows us to set the mode of the directory before using DirBuilder::create to create the directory. We can avoid a TOCTOU problem with creating a directory and setting permissions on that directory in two separate syscalls by doing it in one syscall instead via this method.
Unfortunately, I have not seen an equivalent method on Windows that allows the user to atomically create a directory + set attributes on that directory. And to clarify, I believe it is possible now for users to set file attributes on Windows files and directories via set_permissions* through PermissionExt on Windows (introduced in 1.97). Previous to 1.97, it was only possible to set read only permission/attribute bits on the Permissions struct for Windows, so there might've not been a big concern for TOCTOU problems of creating a directory + setting attributes in two separate syscalls on Windows at that point.
Motivating examples or use cases
I realized that the standard library lacks a way to atomically create a directory and set file attributes on the directory while working on this clippy issue as we suggest for Unix to use DirBuildExt::mode to avoid this TOCTOU issue, but we have no suggestion to provide to Windows.
I think it would be beneficial to multiple libraries to have a function that atomically creates a directory + set some file attributes on that directory in one syscall to avoid a TOCTOU issue in someone potentially replacing the directory with a different file of the same name. This could go for both DirBuilder and Dir on Windows.
Solution sketch
There might be a better solution that what I'm suggesting since I'm not too familiar with Windows (maybe @ChrisDenton might be able to provide better input on how to go about this).
However, what I do understand though is that DirBuilder on Windows creates a directory via CreateDirectoryW, which takes in a path of the directory to be created and a pointer to a security attribute struct. Right now, we always pass in a null pointer for the security attribute argument, which causes the directory to get a default security descriptor. And as far as I understand Security Attributes on Windows are related to Access Control Lists and not file attributes.
Alternatively, I am aware that you can create a directory through NtCreateFile as I've seen from how the Dir struct open and creates a subdirectory relative to the directory it has opened.
I was thinking that perhaps we can do the following:
// in std/src/sys/fs/windows.rs
pub struct DirBuilder {
// Add this attrs field
attrs: u32
}
impl DirBuilder {
...
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
let p = maybe_verbatim(p)?;
// Use NtCreateFile instead of CreateDirectoryW
Ok(())
}
// Introduce this function to mirror what Unix is doing in DirBuilder
pub fn set_file_attributes(&mut self, mask: u32) {
self.attrs = mask;
}
}
// in std/src/os/unix/fs.rs
pub trait DirBuilderExt {
fn file_attributes(&mut self, mask: u32) -> &mut Self;
}
impl DirBuilderExt for fs::DirBuilder {
fn file_attributes(&mut self, mask: u32) -> &mut fs::DirBuilder {
self.as_inner_mut().set_file_attributes(mask);
self
}
}
For Dir, maybe we might want another function called create_dir_with_perms? This is how I was thinking about it:
// in std/src/fs.rs
impl Dir {
pub fn create_dir_with_perms<P: AsRef<Path>>(&self, path: P, perm: Permissions) -> io::Result<()> {
self.inner.create_dir_with_perm(path.as_ref(), perm)
}
}
// in std/src/sys/fs/unix/dir.rs, std/src/sys/fs/windows/dir.rs, std/src/sys/fs/common.rs
impl Dir {
pub fn create_dir_with_perms(&self, path: &Path, perms: FilePermissions) -> io::Result<()> {
run_path_with_cstr(path.as_ref(), &|path| self.create_dir_c(path, perms))
}
// Modify the internal implementation of create_dir_c to take a FilePermissions
fn create_dir_c(&self, path: &CStr, perms: FilePermissions) -> io::Result<()> {
// perms.mode on Unix, perms.attrs on Windows and perms.0 on everything else
cvt(unsafe { mkdirat(self.0.as_raw_fd(), path.as_ptr(), perms.mode) }).map(|_| ())
}
}
Alternatives
As far as I'm aware, there are no other solutions within the standard library that allows the atomic creation of a directory and setting file attributes on Windows.
Links and related work
- Tracking issue for directory handle
- TOCTOU: File creation followed by setting permissions clippy issue
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the Windows filesystem implementation in std/src/sys/fs/windows.rs, the public APIs in std/src/fs.rs, and the related Dir implementation files mentioned in the proposal. Compare CreateDirectoryW and NtCreateFile, then review issues #120426 and rust-clippy#17154. Done means the API shape, platform behavior, and implementation approach have been resolved by the libs-api review.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- operating-systems
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100