rust-lang / rust-lang/libs-team

Add an equivalent `DirBuilderExt` trait on Windows + introducing creating subdirectories from `Dir` with certain permissions upfront

Open
#882 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

api-change-proposal
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

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.