Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create directory if not exists, and return Dir #372

Open
ambiso opened this issue Nov 8, 2024 · 5 comments
Open

Create directory if not exists, and return Dir #372

ambiso opened this issue Nov 8, 2024 · 5 comments

Comments

@ambiso
Copy link

ambiso commented Nov 8, 2024

Is there a simple way to create a directory if it does not yet exist and to return the Dir?

Something along the lines of:

async fn create_dir(parent: &Dir, path: &str) -> anyhow::Result<Dir> {
    match parent.create_dir(path) {
        Ok(_) => {}
        Err(e) => match e.kind() {
            std::io::ErrorKind::AlreadyExists => {}
            _ => {
                Err(e)?;
            }
        },
    }
    Ok(parent.open_dir(path).await?) // could maybe be a TOCTOU?
}

I couldn't find a create_dir_with that would allow specifying e.g. OpenOptions, and all the create_dir functions don't return the Dir itself.

If not, would the project be open to contributions on this?

@sunfishcode
Copy link
Member

The reason for this is that the underying platform APIs don't have such a function. There is no way to atomically obtain a directory handle to a newly-created directory on Unix-family platforms, and possibly not in Windows either. If we added such a function in cap-std, it would have the same TOCTOU hazard.

@ChrisDenton
Copy link

There is no way to atomically obtain a directory handle to a newly-created directory on Unix-family platforms, and possibly not in Windows either

On Windows, NtCreateFile with FILE_DIRECTORY_FILE can create and open in one op. CreateFile can do this too with FILE_FLAG_POSIX_SEMANTICS and FILE_ATTRIBUTE_DIRECTORY.

@sunfishcode
Copy link
Member

Thanks. We still can't do it on Unix though. It's also worth mentioning that cap-std is following std here, where create_dir similarly does not return a handle, presumably for the same reason.

@cgwalters
Copy link
Contributor

If we added such a function in cap-std, it would have the same TOCTOU hazard.

There can only be a TOCTOU if there's already a way for a potentially hostile other process to swap the directory after mkdir, which is only possible if it has write access to the parent, which is only possible if the two processes share a uid or we're talking about a directory like /tmp to simplify.

Let's discard the "same UID" case...anyone trying to isolate while having concurrent write access to the same directory starts to get arbitrarily hard.

For /tmp the sticky bit exists for this reason really...otherwise using it would be impossible.

I can't think of a real world situation where software would be at risk. Certainly it'd make sense to add to the documentation of such a function that it's not guaranteed that "the same" directory that was created was opened...but in practice the people who want to do this are just going to call the two functions anyways since that's all they can do, and I don't see why it makes sense to deny them the "sugar" for such a theoretical problem.

where create_dir similarly does not return a handle,

What "handle" would it return?

@sunfishcode
Copy link
Member

It's not necessarily one Unix user attacking another. It can also be a user typing make and then doing other stuff while they wait. Or a cron job that scanning directories on a system that happens to run at the same time as the admin doing sudo apt purge chromium-browser. These things don't happen frequently, but we can't guarantee that they don't happen.

where create_dir similarly does not return a handle,

What "handle" would it return?

Unix today doesn't give you a handle (or "file descriptor", which I'm using interchangeably in this thread) from mkdir. I speculate that if it did, Rust would have had a Dir type in its standard library by now, and create_dir would return it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants