Skip to main content

starnix_uapi/
open_flags.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::uapi;
6
7// The inner mod is required because bitflags cannot pass the attribute through to the single
8// variant, and attributes cannot be applied to macro invocations.
9mod inner_flags {
10    // Part of the code for the O_RDONLY case that's produced by the macro triggers the lint, but as
11    // a whole, the produced code is still correct.
12    #![allow(clippy::bad_bit_mask)] // TODO(b/303500202) Remove once addressed in bitflags.
13    use super::uapi;
14    use atomic_bitflags::atomic_bitflags;
15
16    atomic_bitflags! {
17        #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
18        pub struct OpenFlags: u32 {
19        const ACCESS_MASK = 0x3;
20
21        // The access modes are not really bits. Instead, they're an enum
22        // embedded in the bitfield. Use ACCESS_MASK to extract the enum
23        // or use the OpenFlags::can_read and OpenFlags::can_write functions.
24        const RDONLY = uapi::O_RDONLY;
25        const WRONLY = uapi::O_WRONLY;
26        const RDWR = uapi::O_RDWR;
27
28        const CREAT = uapi::O_CREAT;
29        const EXCL = uapi::O_EXCL;
30        const NOCTTY = uapi::O_NOCTTY;
31        const TRUNC = uapi::O_TRUNC;
32        const APPEND = uapi::O_APPEND;
33        const NONBLOCK = uapi::O_NONBLOCK;
34        const DSYNC = uapi::O_DSYNC;
35        const ASYNC = uapi::FASYNC;
36        const DIRECT = uapi::O_DIRECT;
37        const LARGEFILE = uapi::O_LARGEFILE;
38        const DIRECTORY = uapi::O_DIRECTORY;
39        const NOFOLLOW = uapi::O_NOFOLLOW;
40        const NOATIME = uapi::O_NOATIME;
41        const CLOEXEC = uapi::O_CLOEXEC;
42        const SYNC = uapi::O_SYNC;
43        const PATH = uapi::O_PATH;
44        const TMPFILE = uapi::O_TMPFILE;
45        const NDELAY = uapi::O_NDELAY;
46        }
47    }
48
49    impl OpenFlags {
50        /// Returns whether the flags allow reading from the open file.
51        ///
52        /// Returns `false` when [`Self::PATH`] is set, as `O_PATH` file descriptors are not opened
53        /// for reading even though `O_RDONLY` is zero.
54        pub fn can_read(&self) -> bool {
55            if self.contains(Self::PATH) {
56                return false;
57            }
58            let access_mode = self.bits() & Self::ACCESS_MASK.bits();
59            access_mode == uapi::O_RDONLY || access_mode == uapi::O_RDWR
60        }
61
62        /// Returns whether the flags allow writing to the open file.
63        ///
64        /// Returns `false` when [`Self::PATH`] is set.
65        pub fn can_write(&self) -> bool {
66            if self.contains(Self::PATH) {
67                return false;
68            }
69            let access_mode = self.bits() & Self::ACCESS_MASK.bits();
70            access_mode == uapi::O_WRONLY || access_mode == uapi::O_RDWR
71        }
72    }
73}
74
75pub use inner_flags::{AtomicOpenFlags, OpenFlags};
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[::fuchsia::test]
82    fn test_can_read_and_can_write() {
83        assert!(OpenFlags::RDONLY.can_read());
84        assert!(!OpenFlags::RDONLY.can_write());
85
86        assert!(!OpenFlags::WRONLY.can_read());
87        assert!(OpenFlags::WRONLY.can_write());
88
89        assert!(OpenFlags::RDWR.can_read());
90        assert!(OpenFlags::RDWR.can_write());
91
92        for mode in [OpenFlags::RDONLY, OpenFlags::WRONLY, OpenFlags::RDWR] {
93            let flags = OpenFlags::PATH | mode;
94            assert!(!flags.can_read(), "{flags:?} should not be readable");
95            assert!(!flags.can_write(), "{flags:?} should not be writable");
96        }
97    }
98}