rust-lang / rust-lang/libs-team

ACP: O_DIRECTORY open flag on OpenOptionsExt

Open
#856 2 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

OpenOptions can only express "open this path only if it is a directory" through custom_flags
and a target-specific constant:

OpenOptions::new().read(true).custom_flags(libc::O_DIRECTORY).open(path)?;

That is a libc dependency or a value you define per target, or handing the open to a crate like
rustix.

Doing the check separately without O_DIRECTORY is not desirable, and could introduce race
conditions or undesirable side effects.

The intent is portable, O_DIRECTORY is POSIX and means the same thing on every unix that has it.
Only the value is platform-specific.

Motivating examples or use cases

Today everyone who needs it has to take a libc dependency, or define the target-specific value
for the flag.
Yazi opens a directory it just created and then works through the descriptor, with custom_flags(O_DIRECTORY | O_NOFOLLOW).

uutils' dd does the same to implement iflag=directory.

None of them would drop libc for this one option, they use other flags too. The benefit of the
proposed API is a named, discoverable option instead of a raw i32, and one that a later
custom_flags call does not overwrite.
Code that reaches for libc only to name this flag could drop the dependency by relying on it.

Solution sketch

// std::os::unix::fs::OpenOptionsExt
/// Sets the option to require that the path is a directory.
///
/// If set, the open passes `O_DIRECTORY` and fails with
/// `io::ErrorKind::NotADirectory` when the path is not a directory.
fn directory(&mut self, directory: bool) -> &mut Self;

Same shape as the other OpenOptions setters, read(bool) and create(bool).

  • No new ErrorKind: ENOTDIR already decodes to NotADirectory.
  • Combinations that cannot work are left to the OS: for example, with .write(true) the open fails
    with EISDIR.
  • A few tier 3 targets have no O_DIRECTORY in libc, see open questions below.

Alternatives

  • custom_flags with the constant from libc, or a crate like rustix performing the open. Both
    approaches add a dependency, or platform-specific handling.
  • A portable option on OpenOptions, suggested before. Opening a directory succeeds
    on unix but needs an extra flag on Windows, so an option could carry that intent instead of
    leaving it to the platform.

Open questions

  • On the tier 3 targets whose libc has no O_DIRECTORY, should directory(true) have no effect
    (silently skip), or should the open fail with ErrorKind::Unsupported? example: std skips
    O_NOFOLLOW on some of them.
  • Naming. directory(bool) follows 842, which named its method after the flag.
    Other names could be considered: require_directory(bool) says what it does more directly but doesn't follow the convention.

Links and related work

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 with the proposed OpenOptionsExt API and the existing Unix handling referenced in library/std/src/sys/fs/unix.rs. Review ACP 842, the tier 3 targets lacking O_DIRECTORY, and the linked prior discussions before evaluating the open questions. Done means the method name, unsupported-platform behavior, and flag interactions have a settled design.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.