cap_primitives/rustix/fs/
oflags.rs

1use crate::fs::{target_o_path, FollowSymlinks, OpenOptions};
2use rustix::fs::OFlags;
3use std::io;
4
5pub(in super::super) fn compute_oflags(options: &OpenOptions) -> io::Result<OFlags> {
6    let mut oflags = OFlags::CLOEXEC;
7    oflags |= get_access_mode(options)?;
8    oflags |= get_creation_mode(options)?;
9    if options.follow == FollowSymlinks::No {
10        oflags |= OFlags::NOFOLLOW;
11    }
12    if options.sync {
13        oflags |= OFlags::SYNC;
14    }
15    if options.dsync {
16        #[cfg(not(target_os = "freebsd"))]
17        {
18            oflags |= OFlags::DSYNC;
19        }
20
21        // Where needed, approximate `DSYNC` with `SYNC`.
22        #[cfg(target_os = "freebsd")]
23        {
24            oflags |= OFlags::SYNC;
25        }
26    }
27    #[cfg(not(any(
28        target_os = "ios",
29        target_os = "macos",
30        target_os = "tvos",
31        target_os = "watchos",
32        target_os = "visionos",
33        target_os = "freebsd",
34        target_os = "fuchsia"
35    )))]
36    if options.rsync {
37        oflags |= OFlags::RSYNC;
38    }
39    if options.nonblock {
40        oflags |= OFlags::NONBLOCK;
41    }
42    if options.dir_required {
43        oflags |= OFlags::DIRECTORY;
44
45        // If the target has `O_PATH`, we don't need to read the directory
46        // entries, and we're not requesting write access (which need to
47        // fail on a directory), use it.
48        if !options.readdir_required && !options.write && !options.append {
49            oflags |= target_o_path();
50        }
51    }
52    // Use `RWMODE` here instead of `ACCMODE` so that we preserve the `O_PATH`
53    // flag.
54    #[cfg(not(target_os = "wasi"))]
55    {
56        oflags |= OFlags::from_bits(options.ext.custom_flags as _).expect("unrecognized OFlags")
57            & !OFlags::RWMODE;
58    }
59    Ok(oflags)
60}
61
62// `OpenOptions` translation code derived from Rust's
63// library/std/src/sys/unix/fs.rs at revision
64// 108e90ca78f052c0c1c49c42a22c85620be19712.
65
66pub(crate) fn get_access_mode(options: &OpenOptions) -> io::Result<OFlags> {
67    match (options.read, options.write, options.append) {
68        (true, false, false) => Ok(OFlags::RDONLY),
69        (false, true, false) => Ok(OFlags::WRONLY),
70        (true, true, false) => Ok(OFlags::RDWR),
71        (false, _, true) => Ok(OFlags::WRONLY | OFlags::APPEND),
72        (true, _, true) => Ok(OFlags::RDWR | OFlags::APPEND),
73        (false, false, false) => Err(rustix::io::Errno::INVAL.into()),
74    }
75}
76
77pub(crate) fn get_creation_mode(options: &OpenOptions) -> io::Result<OFlags> {
78    match (options.write, options.append) {
79        (true, false) => {}
80        (false, false) => {
81            if options.truncate || options.create || options.create_new {
82                return Err(rustix::io::Errno::INVAL.into());
83            }
84        }
85        (_, true) => {
86            if options.truncate && !options.create_new {
87                return Err(rustix::io::Errno::INVAL.into());
88            }
89        }
90    }
91
92    Ok(
93        match (options.create, options.truncate, options.create_new) {
94            (false, false, false) => OFlags::empty(),
95            (true, false, false) => OFlags::CREATE,
96            (false, true, false) => OFlags::TRUNC,
97            (true, true, false) => OFlags::CREATE | OFlags::TRUNC,
98            (_, _, true) => OFlags::CREATE | OFlags::EXCL,
99        },
100    )
101}