cap_primitives/rustix/fs/
is_file_read_write_impl.rs

1use rustix::fd::{AsFd, BorrowedFd};
2use rustix::fs::{fcntl_getfl, OFlags};
3use std::{fs, io};
4
5#[inline]
6pub(crate) fn is_file_read_write_impl(file: &fs::File) -> io::Result<(bool, bool)> {
7    Ok(is_file_read_write(file)?)
8}
9
10/// `fcntl(fd, F_GETFL) & O_ACCMODE`
11///
12/// Returns a pair of booleans indicating whether the file descriptor is
13/// readable and/or writable, respectively. This is only reliable on files; for
14/// example, it doesn't reflect whether sockets have been shut down; for
15/// general I/O handle support, use [`io::is_read_write`].
16#[inline]
17fn is_file_read_write<Fd: AsFd>(fd: Fd) -> io::Result<(bool, bool)> {
18    _is_file_read_write(fd.as_fd())
19}
20
21fn _is_file_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
22    let mode = fcntl_getfl(fd)?;
23
24    // Check for `O_PATH`.
25    #[cfg(any(
26        target_os = "linux",
27        target_os = "android",
28        target_os = "emscripten",
29        target_os = "fuchsia"
30    ))]
31    if mode.contains(OFlags::PATH) {
32        return Ok((false, false));
33    }
34
35    // Use `RWMODE` rather than `ACCMODE` as `ACCMODE` may include `O_PATH`.
36    // We handled `O_PATH` above.
37    match mode & OFlags::RWMODE {
38        OFlags::RDONLY => Ok((true, false)),
39        OFlags::RDWR => Ok((true, true)),
40        OFlags::WRONLY => Ok((false, true)),
41        _ => unreachable!(),
42    }
43}