Skip to main content

linux_api/
fcntl.rs

1use num_enum::{IntoPrimitive, TryFromPrimitive};
2
3use crate::{bindings, const_conversions};
4
5// open(2): The Linux header file <asm/fcntl.h> doesn't define O_ASYNC; the
6// (BSD-derived) FASYNC synonym is defined instead.
7//
8// We define with the LINUX_ prefix for C usage, and as a u32 for consistency
9// with the other c-bindgen output. We use a literal integer here instead of
10// setting directly to LINUX_FASYNC so that cbindgen can handle it.
11pub const LINUX_O_ASYNC: u32 = 0x2000;
12const _: () = const { assert!(LINUX_O_ASYNC == bindings::LINUX_FASYNC) };
13
14bitflags::bitflags! {
15    /// Open flags, as used e.g. with `open`.
16    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
17    pub struct OFlag: i32 {
18        const O_ACCMODE = const_conversions::i32_from_u32(bindings::LINUX_O_ACCMODE);
19        const O_RDONLY = const_conversions::i32_from_u32(bindings::LINUX_O_RDONLY);
20        const O_WRONLY = const_conversions::i32_from_u32(bindings::LINUX_O_WRONLY);
21        const O_RDWR = const_conversions::i32_from_u32(bindings::LINUX_O_RDWR);
22        const O_CREAT = const_conversions::i32_from_u32(bindings::LINUX_O_CREAT);
23        const O_EXCL = const_conversions::i32_from_u32(bindings::LINUX_O_EXCL);
24        const O_NOCTTY = const_conversions::i32_from_u32(bindings::LINUX_O_NOCTTY);
25        const O_TRUNC = const_conversions::i32_from_u32(bindings::LINUX_O_TRUNC);
26        const O_APPEND = const_conversions::i32_from_u32(bindings::LINUX_O_APPEND);
27        const O_NONBLOCK = const_conversions::i32_from_u32(bindings::LINUX_O_NONBLOCK);
28        const O_DSYNC = const_conversions::i32_from_u32(bindings::LINUX_O_DSYNC);
29        const O_DIRECT = const_conversions::i32_from_u32(bindings::LINUX_O_DIRECT);
30        const O_LARGEFILE = const_conversions::i32_from_u32(bindings::LINUX_O_LARGEFILE);
31        const O_DIRECTORY = const_conversions::i32_from_u32(bindings::LINUX_O_DIRECTORY);
32        const O_NOFOLLOW = const_conversions::i32_from_u32(bindings::LINUX_O_NOFOLLOW);
33        const O_NOATIME = const_conversions::i32_from_u32(bindings::LINUX_O_NOATIME);
34        const O_CLOEXEC = const_conversions::i32_from_u32(bindings::LINUX_O_CLOEXEC);
35        const O_SYNC = const_conversions::i32_from_u32(bindings::LINUX_O_SYNC);
36        const O_PATH = const_conversions::i32_from_u32(bindings::LINUX_O_PATH);
37        const O_TMPFILE = const_conversions::i32_from_u32(bindings::LINUX_O_TMPFILE);
38        const O_NDELAY = const_conversions::i32_from_u32(bindings::LINUX_O_NDELAY);
39        const O_ASYNC = const_conversions::i32_from_u32(LINUX_O_ASYNC);
40    }
41}
42
43impl OFlag {
44    pub fn access_mode_flags(&self) -> AccessModeOFlag {
45        AccessModeOFlag::from_bits_truncate(self.bits())
46    }
47
48    pub fn file_creation_flags(&self) -> FileCreationOFlag {
49        FileCreationOFlag::from_bits_truncate(self.bits())
50    }
51
52    pub fn file_status_flags(&self) -> FileStatusOFlag {
53        FileStatusOFlag::from_bits_truncate(self.bits())
54    }
55
56    /// Split into access-mode, creation, and status flags.
57    pub fn partition(&self) -> (AccessModeOFlag, FileCreationOFlag, FileStatusOFlag) {
58        (
59            self.access_mode_flags(),
60            self.file_creation_flags(),
61            self.file_status_flags(),
62        )
63    }
64}
65
66impl From<AccessModeOFlag> for OFlag {
67    fn from(value: AccessModeOFlag) -> Self {
68        Self::from_bits(value.bits()).unwrap()
69    }
70}
71
72impl From<FileCreationOFlag> for OFlag {
73    fn from(value: FileCreationOFlag) -> Self {
74        Self::from_bits(value.bits()).unwrap()
75    }
76}
77
78impl From<FileStatusOFlag> for OFlag {
79    fn from(value: FileStatusOFlag) -> Self {
80        Self::from_bits(value.bits()).unwrap()
81    }
82}
83
84bitflags::bitflags! {
85    /// "Access mode" flags as specified in open(2); Subset of OFlag.
86    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
87    pub struct AccessModeOFlag: i32 {
88        const O_RDONLY = OFlag::O_RDONLY.bits();
89        const O_WRONLY = OFlag::O_WRONLY.bits();
90        const O_RDWR = OFlag::O_RDWR.bits();
91    }
92}
93
94bitflags::bitflags! {
95    /// "File creation flags" as specified in open(2). Subset of OFlag.
96    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
97    pub struct FileCreationOFlag : i32 {
98        const O_CLOEXEC = OFlag::O_CLOEXEC.bits();
99        const O_CREAT= OFlag::O_CREAT.bits();
100        const O_DIRECTORY= OFlag::O_DIRECTORY.bits();
101        const O_EXCL= OFlag::O_EXCL.bits();
102        const O_NOCTTY= OFlag::O_NOCTTY.bits();
103        const O_NOFOLLOW= OFlag::O_NOFOLLOW.bits();
104        const O_TMPFILE= OFlag::O_TMPFILE.bits();
105        const O_TRUNC= OFlag::O_TRUNC.bits();
106    }
107}
108
109bitflags::bitflags! {
110    /// "File status flags" as specified in open(2), as "all the remaining flags"
111    /// that aren't specified as creation or access flags.
112    ///
113    /// open(2): "The file status flags can be retrieved and (in some cases)
114    /// modified; see fcntl(2) for details."
115    ///
116    /// Subset of OFlag.
117    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
118    pub struct FileStatusOFlag: i32 {
119        const O_APPEND= OFlag::O_APPEND.bits();
120        const O_ASYNC= OFlag::O_ASYNC.bits();
121        const O_DIRECT= OFlag::O_DIRECT.bits();
122        const O_NOATIME= OFlag::O_NOATIME.bits();
123        const O_NONBLOCK= OFlag::O_NONBLOCK.bits();
124        const O_DSYNC= OFlag::O_DSYNC.bits();
125        const O_SYNC= OFlag::O_SYNC.bits();
126        const O_LARGEFILE= OFlag::O_LARGEFILE.bits();
127        const O_PATH= OFlag::O_PATH.bits();
128    }
129}
130
131impl FileStatusOFlag {
132    pub fn as_o_flags(&self) -> OFlag {
133        OFlag::from_bits(self.bits()).unwrap()
134    }
135
136    /// Returns a tuple of the `FileStatus` and any remaining flags.
137    pub fn from_o_flags(flags: OFlag) -> (Self, OFlag) {
138        let status = Self::from_bits_truncate(flags.bits());
139        let remaining = flags.bits() & !status.bits();
140        (status, OFlag::from_bits(remaining).unwrap())
141    }
142}
143
144/// fcntl commands, as used with `fcntl`.
145#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
146#[repr(u32)]
147#[allow(non_camel_case_types)]
148pub enum FcntlCommand {
149    F_DUPFD = bindings::LINUX_F_DUPFD,
150    F_GETFD = bindings::LINUX_F_GETFD,
151    F_SETFD = bindings::LINUX_F_SETFD,
152    F_GETFL = bindings::LINUX_F_GETFL,
153    F_SETFL = bindings::LINUX_F_SETFL,
154    F_GETLK = bindings::LINUX_F_GETLK,
155    F_SETLK = bindings::LINUX_F_SETLK,
156    F_SETLKW = bindings::LINUX_F_SETLKW,
157    F_SETOWN = bindings::LINUX_F_SETOWN,
158    F_GETOWN = bindings::LINUX_F_GETOWN,
159    F_SETSIG = bindings::LINUX_F_SETSIG,
160    F_GETSIG = bindings::LINUX_F_GETSIG,
161    F_SETOWN_EX = bindings::LINUX_F_SETOWN_EX,
162    F_GETOWN_EX = bindings::LINUX_F_GETOWN_EX,
163    F_GETOWNER_UIDS = bindings::LINUX_F_GETOWNER_UIDS,
164    F_OFD_GETLK = bindings::LINUX_F_OFD_GETLK,
165    F_OFD_SETLK = bindings::LINUX_F_OFD_SETLK,
166    F_OFD_SETLKW = bindings::LINUX_F_OFD_SETLKW,
167    F_SETLEASE = bindings::LINUX_F_SETLEASE,
168    F_GETLEASE = bindings::LINUX_F_GETLEASE,
169    F_NOTIFY = bindings::LINUX_F_NOTIFY,
170    F_DUPFD_QUERY = bindings::LINUX_F_DUPFD_QUERY,
171    F_CREATED_QUERY = bindings::LINUX_F_CREATED_QUERY,
172    F_DUPFD_CLOEXEC = bindings::LINUX_F_DUPFD_CLOEXEC,
173    F_SETPIPE_SZ = bindings::LINUX_F_SETPIPE_SZ,
174    F_GETPIPE_SZ = bindings::LINUX_F_GETPIPE_SZ,
175    F_ADD_SEALS = bindings::LINUX_F_ADD_SEALS,
176    F_GET_SEALS = bindings::LINUX_F_GET_SEALS,
177    F_CANCELLK = bindings::LINUX_F_CANCELLK,
178    F_GET_RW_HINT = bindings::LINUX_F_GET_RW_HINT,
179    F_SET_RW_HINT = bindings::LINUX_F_SET_RW_HINT,
180    F_GET_FILE_RW_HINT = bindings::LINUX_F_GET_FILE_RW_HINT,
181    F_SET_FILE_RW_HINT = bindings::LINUX_F_SET_FILE_RW_HINT,
182    F_GETDELEG = bindings::LINUX_F_GETDELEG,
183    F_SETDELEG = bindings::LINUX_F_SETDELEG,
184}
185
186pub use bindings::linux_flock as flock;
187unsafe impl shadow_pod::Pod for flock {}
188
189// Valid values for `flock::l_whence`.
190#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
191#[repr(i16)]
192#[allow(non_camel_case_types)]
193pub enum FlockWhence {
194    SEEK_SET = const_conversions::i16_from_u32(bindings::LINUX_SEEK_SET),
195    SEEK_CUR = const_conversions::i16_from_u32(bindings::LINUX_SEEK_CUR),
196    SEEK_END = const_conversions::i16_from_u32(bindings::LINUX_SEEK_END),
197}
198
199/// Owner, as used with [`FcntlCommand::F_SETOWN_EX`] and [`FcntlCommand::F_GETOWN_EX`]
200#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
201#[repr(u32)]
202#[allow(non_camel_case_types)]
203pub enum FcntlOwner {
204    F_OWNER_TID = bindings::LINUX_F_OWNER_TID,
205    F_OWNER_PID = bindings::LINUX_F_OWNER_PID,
206    F_OWNER_PGRP = bindings::LINUX_F_OWNER_PGRP,
207}
208
209/// Lease type, as used with [`FcntlCommand::F_SETLEASE`]
210#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
211#[repr(u32)]
212#[allow(non_camel_case_types)]
213pub enum FcntlLeaseType {
214    F_RDLCK = bindings::LINUX_F_RDLCK,
215    F_WRLCK = bindings::LINUX_F_WRLCK,
216    F_UNLCK = bindings::LINUX_F_UNLCK,
217    F_EXLCK = bindings::LINUX_F_EXLCK,
218    F_SHLCK = bindings::LINUX_F_SHLCK,
219}
220
221/// Lock type, as found in `flock::l_type`, used with e.g.
222/// [`FcntlCommand::F_SETLK`] and [`FcntlCommand::F_OFD_SETLK`]
223#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
224#[repr(i16)]
225#[allow(non_camel_case_types)]
226pub enum FlockType {
227    F_RDLCK = const_conversions::i16_from_u32(bindings::LINUX_F_RDLCK),
228    F_WRLCK = const_conversions::i16_from_u32(bindings::LINUX_F_WRLCK),
229    F_UNLCK = const_conversions::i16_from_u32(bindings::LINUX_F_UNLCK),
230}
231
232/// Seal type, as used with [`FcntlCommand::F_ADD_SEALS`] and [`FcntlCommand::F_GET_SEALS`].
233#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
234#[repr(u32)]
235#[allow(non_camel_case_types)]
236pub enum FcntlSealType {
237    F_SEAL_SEAL = bindings::LINUX_F_SEAL_SEAL,
238    F_SEAL_SHRINK = bindings::LINUX_F_SEAL_SHRINK,
239    F_SEAL_GROW = bindings::LINUX_F_SEAL_GROW,
240    F_SEAL_WRITE = bindings::LINUX_F_SEAL_WRITE,
241    F_SEAL_FUTURE_WRITE = bindings::LINUX_F_SEAL_FUTURE_WRITE,
242    F_SEAL_EXEC = bindings::LINUX_F_SEAL_EXEC,
243}
244
245/// Read-write hint, as used with [`FcntlCommand::F_GET_RW_HINT`] and [`FcntlCommand::F_SET_RW_HINT`].
246#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
247#[repr(u32)]
248#[allow(non_camel_case_types)]
249pub enum FcntlRwHint {
250    RWH_WRITE_LIFE_NOT_SET = bindings::LINUX_RWH_WRITE_LIFE_NOT_SET,
251    RWH_WRITE_LIFE_NONE = bindings::LINUX_RWH_WRITE_LIFE_NONE,
252    RWH_WRITE_LIFE_SHORT = bindings::LINUX_RWH_WRITE_LIFE_SHORT,
253    RWH_WRITE_LIFE_MEDIUM = bindings::LINUX_RWH_WRITE_LIFE_MEDIUM,
254    RWH_WRITE_LIFE_LONG = bindings::LINUX_RWH_WRITE_LIFE_LONG,
255    RWH_WRITE_LIFE_EXTREME = bindings::LINUX_RWH_WRITE_LIFE_EXTREME,
256}
257
258bitflags::bitflags! {
259    /// Descriptor flags, as used with [`FcntlCommand::F_GETFL`] and [`FcntlCommand::F_SETFL`].
260    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
261    pub struct DescriptorFlags: i32 {
262        const FD_CLOEXEC = const_conversions::i32_from_u32(bindings::LINUX_FD_CLOEXEC);
263    }
264}
265
266impl DescriptorFlags {
267    pub fn as_o_flags(&self) -> OFlag {
268        let mut flags = OFlag::empty();
269        if self.contains(Self::FD_CLOEXEC) {
270            flags.insert(OFlag::O_CLOEXEC);
271        }
272        flags
273    }
274
275    /// Returns a tuple of the `DescriptorFlags` and any remaining flags.
276    pub fn from_o_flags(flags: OFlag) -> (Self, OFlag) {
277        let mut remaining = flags;
278        let mut flags = Self::empty();
279
280        if remaining.contains(OFlag::O_CLOEXEC) {
281            remaining.remove(OFlag::O_CLOEXEC);
282            flags.insert(Self::FD_CLOEXEC);
283        }
284
285        (flags, remaining)
286    }
287}
288
289bitflags::bitflags! {
290    /// flags for execveat.
291    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
292    pub struct ExecveAtFlags: i32 {
293        const AT_EMPTY_PATH = const_conversions::i32_from_u32(bindings::LINUX_AT_EMPTY_PATH);
294        const AT_SYMLINK_NOFOLLOW = const_conversions::i32_from_u32(bindings::LINUX_AT_SYMLINK_NOFOLLOW);
295        const AT_EXECVE_CHECK = const_conversions::i32_from_u32(bindings::LINUX_AT_EXECVE_CHECK);
296    }
297}
298
299#[cfg(test)]
300mod test {
301    use super::*;
302
303    #[test]
304    fn test_partition_oflags() {
305        let all_o_flags = OFlag::all();
306        let (access_mode, creation, status) = all_o_flags.partition();
307
308        // all defined bits should be set in partitions.
309        assert_eq!(access_mode, AccessModeOFlag::all());
310        assert_eq!(creation, FileCreationOFlag::all());
311        assert_eq!(status, FileStatusOFlag::all());
312
313        // convert back to OFlag.
314        let access_mode = OFlag::from(access_mode);
315        let creation = OFlag::from(creation);
316        let status = OFlag::from(status);
317
318        // they should all be mutually exclusive.
319        assert_eq!(access_mode & creation, OFlag::empty());
320        assert_eq!(access_mode & status, OFlag::empty());
321        assert_eq!(creation & status, OFlag::empty());
322
323        // together they should cover all of the OFlag bits.
324        assert_eq!(
325            OFlag::all() - (access_mode | creation | status),
326            OFlag::empty()
327        );
328    }
329}
330
331mod export {
332    /// "file creation" "oflags", as per `open(2)`.
333    // TODO: export as a plain constant, if/when cbindgen handles evaluation of
334    // const functions.
335    #[unsafe(no_mangle)]
336    pub extern "C-unwind" fn linux_file_creation_oflags() -> core::ffi::c_int {
337        super::FileCreationOFlag::all().bits()
338    }
339
340    /// "access mode" "oflags", as per `open(2)`.
341    // TODO: export as a plain constant, if/when cbindgen handles evaluation of
342    // const functions.
343    #[unsafe(no_mangle)]
344    pub extern "C-unwind" fn linux_access_mode_oflags() -> core::ffi::c_int {
345        super::AccessModeOFlag::all().bits()
346    }
347
348    /// "file status" "oflags", as per `open(2)`.
349    // TODO: export as a plain constant, if/when cbindgen handles evaluation of
350    // const functions.
351    #[unsafe(no_mangle)]
352    pub extern "C-unwind" fn linux_file_status_oflags() -> core::ffi::c_int {
353        super::FileStatusOFlag::all().bits()
354    }
355}