Skip to main content

shadow_rs/host/descriptor/
mod.rs

1//! Linux file descriptors and file descriptions (equivalent to Linux `struct file`s).
2
3use std::sync::Arc;
4
5use crate::cshadow as c;
6use crate::host::descriptor::listener::{StateListenHandle, StateListenerFilter};
7use crate::host::descriptor::socket::{Socket, SocketRef, SocketRefMut};
8use crate::host::fcntl_lock_table::FileId;
9use crate::host::host::Host;
10use crate::host::memory_manager::MemoryManager;
11use crate::host::process::ProcessId;
12use crate::host::syscall::io::IoVec;
13use crate::host::syscall::types::{SyscallError, SyscallResult};
14use crate::utility::callback_queue::CallbackQueue;
15use crate::utility::{HostTreePointer, IsSend, IsSync, ObjectCounter};
16use atomic_refcell::AtomicRefCell;
17use linux_api::errno::Errno;
18use linux_api::fcntl::{DescriptorFlags, OFlag};
19use linux_api::ioctls::IoctlRequest;
20use shadow_shim_helper_rs::explicit_drop::ExplicitDrop;
21use shadow_shim_helper_rs::syscall_types::ForeignPtr;
22
23pub mod descriptor_table;
24pub mod epoll;
25pub mod eventfd;
26pub mod listener;
27pub mod pipe;
28pub mod shared_buf;
29pub mod socket;
30pub mod timerfd;
31
32// TODO: migrate users to use `FileStatusOFlag` directly.
33pub use linux_api::fcntl::FileStatusOFlag as FileStatus;
34/// For functions that involve closing descriptors; specifies for which
35/// `ProcessId` posix record locks ought to be freed, if any.
36#[derive(Copy, Clone, Eq, PartialEq, Debug)]
37pub enum DropPosixRecordLocks {
38    // Indicates to *not* drop posix record locks as part of closing a
39    // descriptor.  This should only be used for "non-user-visible" closes; e.g.
40    // we end up internally cloning and dropping descriptors as part of our fork
41    // and exec implementations.
42    False,
43    // Includes associated ProcessId for which to drop locks.
44    ForPid(ProcessId),
45}
46
47bitflags::bitflags! {
48    /// These are flags that should generally not change (analagous to the Linux `filp->f_mode`).
49    /// Since the plugin will never see these values and they're not exposed by the kernel, we
50    /// don't match the kernel `FMODE_` values here.
51    ///
52    /// Examples: https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L111
53    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
54    pub struct FileMode: u32 {
55        const READ = 0b00000001;
56        const WRITE = 0b00000010;
57    }
58}
59
60impl FileMode {
61    pub fn as_o_flags(&self) -> OFlag {
62        const READ_AND_WRITE: FileMode = FileMode::READ.union(FileMode::WRITE);
63        const EMPTY: FileMode = FileMode::empty();
64
65        // https://www.gnu.org/software/libc/manual/html_node/Access-Modes.html
66        match *self {
67            READ_AND_WRITE => OFlag::O_RDWR,
68            Self::READ => OFlag::O_RDONLY,
69            Self::WRITE => OFlag::O_WRONLY,
70            // a linux-specific flag
71            EMPTY => OFlag::O_PATH,
72            _ => panic!("Invalid file mode flags"),
73        }
74    }
75
76    /// Returns a tuple of the [`FileMode`] and any remaining flags, or an empty `Err` if the flags
77    /// aren't valid (for example specifying both `O_RDWR` and `O_WRONLY`).
78    #[allow(clippy::result_unit_err)]
79    pub fn from_o_flags(flags: OFlag) -> Result<(Self, OFlag), ()> {
80        // apply the access mode mask (the O_PATH flag is not contained within the access
81        // mode mask, so we need to add it separately)
82        let mode = flags & (OFlag::O_ACCMODE | OFlag::O_PATH);
83        let remaining = flags - (OFlag::O_ACCMODE | OFlag::O_PATH);
84
85        // https://www.gnu.org/software/libc/manual/html_node/Access-Modes.html
86        let mode = match mode {
87            OFlag::O_RDONLY => FileMode::READ,
88            OFlag::O_WRONLY => FileMode::WRITE,
89            OFlag::O_RDWR => FileMode::READ | FileMode::WRITE,
90            OFlag::O_PATH => FileMode::empty(),
91            _ => return Err(()),
92        };
93
94        Ok((mode, remaining))
95    }
96}
97
98bitflags::bitflags! {
99    /// Flags representing the state of a file.
100    ///
101    /// Listeners can subscribe to state changes using [`FileRefMut::add_listener`] (or similar
102    /// methods on [`SocketRefMut`][socket::SocketRefMut], [`Pipe`][pipe::Pipe], etc).
103    ///
104    /// When setting these flags on a file, they should match the result of an epoll-wait on the
105    /// file. For example if an epoll-wait on a file would return `EPOLLIN`, then the file should
106    /// have the `READABLE` state flag. If an epoll-wait would *not* return `EPOLLIN`, then the file
107    /// should not have the `READABLE` state flag.
108    #[derive(Default, Copy, Clone, Debug)]
109    #[repr(transparent)]
110    pub struct FileState: u16 {
111        // remove this when the last reference to `FileState_NONE` has been removed from the C code
112        #[deprecated(note = "use `FileState::empty()`")]
113        const NONE = 0;
114        /// Has been initialized and it is now OK to unblock any plugin waiting on a particular
115        /// state.
116        ///
117        /// This is a legacy C state and is deprecated.
118        const ACTIVE = 1 << 0;
119        /// Can be read, i.e. there is data waiting for user.
120        const READABLE = 1 << 1;
121        /// Can be written, i.e. there is available buffer space.
122        const WRITABLE = 1 << 2;
123        /// User already called close.
124        const CLOSED = 1 << 3;
125        /// A wakeup operation occurred on a futex.
126        const FUTEX_WAKEUP = 1 << 4;
127        /// A child process had an event reportable via e.g. waitpid.
128        const CHILD_EVENT = 1 << 5;
129        /// A listening socket is allowing connections. Only applicable to connection-oriented unix
130        /// sockets.
131        const SOCKET_ALLOWING_CONNECT = 1 << 6;
132        /// "read hangup" - Stream socket peer has shut down connection for
133        /// writing (or completely closed it), as for EPOLLRDHUP.
134        const RDHUP = 1 << 7;
135    }
136}
137
138bitflags::bitflags! {
139    /// File-related signals that listeners can watch for.
140    #[derive(Default, Copy, Clone, Debug)]
141    #[repr(transparent)]
142    pub struct FileSignals: u32 {
143        /// The read buffer now has additional data available to read.
144        const READ_BUFFER_GREW = 1 << 0;
145    }
146}
147
148/// A wrapper for any type of file object.
149#[derive(Clone)]
150pub enum File {
151    Pipe(Arc<AtomicRefCell<pipe::Pipe>>),
152    EventFd(Arc<AtomicRefCell<eventfd::EventFd>>),
153    Socket(Socket),
154    TimerFd(Arc<AtomicRefCell<timerfd::TimerFd>>),
155    Epoll(Arc<AtomicRefCell<epoll::Epoll>>),
156}
157
158// will not compile if `File` is not Send + Sync
159impl IsSend for File {}
160impl IsSync for File {}
161
162impl File {
163    pub fn borrow(&self) -> FileRef<'_> {
164        match self {
165            Self::Pipe(f) => FileRef::Pipe(f.borrow()),
166            Self::EventFd(f) => FileRef::EventFd(f.borrow()),
167            Self::Socket(f) => FileRef::Socket(f.borrow()),
168            Self::TimerFd(f) => FileRef::TimerFd(f.borrow()),
169            Self::Epoll(f) => FileRef::Epoll(f.borrow()),
170        }
171    }
172
173    pub fn try_borrow(&self) -> Result<FileRef<'_>, atomic_refcell::BorrowError> {
174        Ok(match self {
175            Self::Pipe(f) => FileRef::Pipe(f.try_borrow()?),
176            Self::EventFd(f) => FileRef::EventFd(f.try_borrow()?),
177            Self::Socket(f) => FileRef::Socket(f.try_borrow()?),
178            Self::TimerFd(f) => FileRef::TimerFd(f.try_borrow()?),
179            Self::Epoll(f) => FileRef::Epoll(f.try_borrow()?),
180        })
181    }
182
183    pub fn borrow_mut(&self) -> FileRefMut<'_> {
184        match self {
185            Self::Pipe(f) => FileRefMut::Pipe(f.borrow_mut()),
186            Self::EventFd(f) => FileRefMut::EventFd(f.borrow_mut()),
187            Self::Socket(f) => FileRefMut::Socket(f.borrow_mut()),
188            Self::TimerFd(f) => FileRefMut::TimerFd(f.borrow_mut()),
189            Self::Epoll(f) => FileRefMut::Epoll(f.borrow_mut()),
190        }
191    }
192
193    pub fn try_borrow_mut(&self) -> Result<FileRefMut<'_>, atomic_refcell::BorrowMutError> {
194        Ok(match self {
195            Self::Pipe(f) => FileRefMut::Pipe(f.try_borrow_mut()?),
196            Self::EventFd(f) => FileRefMut::EventFd(f.try_borrow_mut()?),
197            Self::Socket(f) => FileRefMut::Socket(f.try_borrow_mut()?),
198            Self::TimerFd(f) => FileRefMut::TimerFd(f.try_borrow_mut()?),
199            Self::Epoll(f) => FileRefMut::Epoll(f.try_borrow_mut()?),
200        })
201    }
202
203    pub fn canonical_handle(&self) -> usize {
204        match self {
205            Self::Pipe(f) => Arc::as_ptr(f) as usize,
206            Self::EventFd(f) => Arc::as_ptr(f) as usize,
207            Self::Socket(f) => f.canonical_handle(),
208            Self::TimerFd(f) => Arc::as_ptr(f) as usize,
209            Self::Epoll(f) => Arc::as_ptr(f) as usize,
210        }
211    }
212}
213
214impl std::fmt::Debug for File {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        match self {
217            Self::Pipe(_) => write!(f, "Pipe")?,
218            Self::EventFd(_) => write!(f, "EventFd")?,
219            Self::Socket(_) => write!(f, "Socket")?,
220            Self::TimerFd(_) => write!(f, "TimerFd")?,
221            Self::Epoll(_) => write!(f, "Epoll")?,
222        }
223
224        if let Ok(file) = self.try_borrow() {
225            let state = file.state();
226            let status = file.status();
227            write!(f, "(state: {state:?}, status: {status:?})")
228        } else {
229            write!(f, "(already borrowed)")
230        }
231    }
232}
233
234/// Wraps an immutably borrowed [`File`]. Created from [`File::borrow`] or [`File::try_borrow`].
235pub enum FileRef<'a> {
236    Pipe(atomic_refcell::AtomicRef<'a, pipe::Pipe>),
237    EventFd(atomic_refcell::AtomicRef<'a, eventfd::EventFd>),
238    Socket(SocketRef<'a>),
239    TimerFd(atomic_refcell::AtomicRef<'a, timerfd::TimerFd>),
240    Epoll(atomic_refcell::AtomicRef<'a, epoll::Epoll>),
241}
242
243/// Wraps a mutably borrowed [`File`]. Created from [`File::borrow_mut`] or
244/// [`File::try_borrow_mut`].
245pub enum FileRefMut<'a> {
246    Pipe(atomic_refcell::AtomicRefMut<'a, pipe::Pipe>),
247    EventFd(atomic_refcell::AtomicRefMut<'a, eventfd::EventFd>),
248    Socket(SocketRefMut<'a>),
249    TimerFd(atomic_refcell::AtomicRefMut<'a, timerfd::TimerFd>),
250    Epoll(atomic_refcell::AtomicRefMut<'a, epoll::Epoll>),
251}
252
253impl FileRef<'_> {
254    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
255        pub fn state(&self) -> FileState
256    );
257    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
258        pub fn mode(&self) -> FileMode
259    );
260    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
261        pub fn status(&self) -> FileStatus
262    );
263    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
264        pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError>
265    );
266    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
267        pub fn has_open_file(&self) -> bool
268    );
269    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
270        pub fn supports_sa_restart(&self) -> bool
271    );
272}
273
274impl FileRefMut<'_> {
275    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
276        pub fn state(&self) -> FileState
277    );
278    enum_passthrough!(self, (off, whence), Pipe, EventFd, Socket, TimerFd, Epoll;
279        pub fn lseek(&mut self, off: linux_api::posix_types::kernel_off_t, whence: linux_api::unistd::LSeekWhence) -> Result<linux_api::posix_types::kernel_off_t, SyscallError>
280    );
281    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
282        pub fn mode(&self) -> FileMode
283    );
284    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
285        pub fn status(&self) -> FileStatus
286    );
287    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
288        pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError>
289    );
290    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
291        pub fn has_open_file(&self) -> bool
292    );
293    enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
294        pub fn supports_sa_restart(&self) -> bool
295    );
296    enum_passthrough!(self, (val), Pipe, EventFd, Socket, TimerFd, Epoll;
297        pub fn set_has_open_file(&mut self, val: bool)
298    );
299    enum_passthrough!(self, (cb_queue), Pipe, EventFd, Socket, TimerFd, Epoll;
300        pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError>
301    );
302    enum_passthrough!(self, (status), Pipe, EventFd, Socket, TimerFd, Epoll;
303        pub fn set_status(&mut self, status: FileStatus)
304    );
305    enum_passthrough!(self, (request, arg_ptr, memory_manager), Pipe, EventFd, Socket, TimerFd, Epoll;
306        pub fn ioctl(&mut self, request: IoctlRequest, arg_ptr: ForeignPtr<()>, memory_manager: &mut MemoryManager) -> SyscallResult
307    );
308    enum_passthrough!(self, (monitoring_state, monitoring_signals, filter, notify_fn), Pipe, EventFd, Socket, TimerFd, Epoll;
309        pub fn add_listener(
310            &mut self,
311            monitoring_state: FileState,
312            monitoring_signals: FileSignals,
313            filter: StateListenerFilter,
314            notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue) + Send + Sync + 'static,
315        ) -> StateListenHandle
316    );
317    enum_passthrough!(self, (ptr), Pipe, EventFd, Socket, TimerFd, Epoll;
318        pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>)
319    );
320    enum_passthrough!(self, (ptr), Pipe, EventFd, Socket, TimerFd, Epoll;
321        pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener)
322    );
323    enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Pipe, EventFd, Socket, TimerFd, Epoll;
324        pub fn readv(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
325                     mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
326    );
327    enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Pipe, EventFd, Socket, TimerFd, Epoll;
328        pub fn writev(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
329                      mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
330    );
331}
332
333impl std::fmt::Debug for FileRef<'_> {
334    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335        match self {
336            Self::Pipe(_) => write!(f, "Pipe")?,
337            Self::EventFd(_) => write!(f, "EventFd")?,
338            Self::Socket(_) => write!(f, "Socket")?,
339            Self::TimerFd(_) => write!(f, "TimerFd")?,
340            Self::Epoll(_) => write!(f, "Epoll")?,
341        }
342
343        let state = self.state();
344        let status = self.status();
345        write!(f, "(state: {state:?}, status: {status:?})")
346    }
347}
348
349impl std::fmt::Debug for FileRefMut<'_> {
350    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351        match self {
352            Self::Pipe(_) => write!(f, "Pipe")?,
353            Self::EventFd(_) => write!(f, "EventFd")?,
354            Self::Socket(_) => write!(f, "Socket")?,
355            Self::TimerFd(_) => write!(f, "TimerFd")?,
356            Self::Epoll(_) => write!(f, "Epoll")?,
357        }
358
359        let state = self.state();
360        let status = self.status();
361        write!(f, "(state: {state:?}, status: {status:?})")
362    }
363}
364
365/// Represents a POSIX file description, or a Linux `struct file`.
366///
367/// An `OpenFile` wraps a reference to a [`File`]. Once there are no more `OpenFile` objects for a
368/// given `File`, the `File` will be closed. Typically this means that holding an `OpenFile` will
369/// ensure that the file remains open (the file's status will not become [`FileState::CLOSED`]), but
370/// the underlying file may close itself in extenuating circumstances (for example if the file has
371/// an internal error).
372///
373/// **Warning:** If an `OpenFile` for a specific file already exists, it is an error to create a new
374/// `OpenFile` for that file. You must clone the existing `OpenFile` object. A new `OpenFile` object
375/// should probably only ever be created for a newly created file object. Otherwise for existing
376/// file objects, it won't be clear if there are already-existing `OpenFile` objects for that file.
377///
378/// There must also not be any existing mutable borrows of the file when an `OpenFile` is created.
379#[derive(Clone, Debug)]
380pub struct OpenFile {
381    inner: Arc<OpenFileInner>,
382    _counter: ObjectCounter,
383}
384
385// will not compile if `OpenFile` is not Send + Sync
386impl IsSend for OpenFile {}
387impl IsSync for OpenFile {}
388
389impl OpenFile {
390    pub fn new(file: File) -> Self {
391        {
392            let mut file = file.borrow_mut();
393
394            if file.state().contains(FileState::CLOSED) {
395                // panic if debug assertions are enabled
396                warn_and_debug_panic!("Creating an `OpenFile` object for a closed file");
397            }
398
399            if file.has_open_file() {
400                // panic if debug assertions are enabled
401                warn_and_debug_panic!(
402                    "Creating an `OpenFile` object for a file that already has an `OpenFile` object"
403                );
404            }
405
406            file.set_has_open_file(true);
407        }
408
409        Self {
410            inner: Arc::new(OpenFileInner::new(file)),
411            _counter: ObjectCounter::new("OpenFile"),
412        }
413    }
414
415    pub fn inner_file(&self) -> &File {
416        self.inner.file.as_ref().unwrap()
417    }
418
419    /// Will close the inner `File` object if this is the last `OpenFile` for that `File`. This
420    /// behaviour is the same as simply dropping this `OpenFile` object, but allows you to pass an
421    /// event queue and get the return value of the close operation.
422    pub fn close(self, cb_queue: &mut CallbackQueue) -> Option<Result<(), SyscallError>> {
423        let OpenFile { inner, _counter } = self;
424
425        // if this is the last reference, call close() on the file
426        Arc::into_inner(inner).map(|inner| inner.close(cb_queue))
427    }
428}
429
430#[derive(Clone, Debug)]
431struct OpenFileInner {
432    file: Option<File>,
433    _counter: ObjectCounter,
434}
435
436impl OpenFileInner {
437    pub fn new(file: File) -> Self {
438        Self {
439            file: Some(file),
440            _counter: ObjectCounter::new("OpenFileInner"),
441        }
442    }
443
444    pub fn close(mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
445        self.close_helper(cb_queue)
446    }
447
448    fn close_helper(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
449        if let Some(file) = self.file.take() {
450            file.borrow_mut().close(cb_queue)?;
451        }
452        Ok(())
453    }
454}
455
456impl std::ops::Drop for OpenFileInner {
457    fn drop(&mut self) {
458        // ignore any return value
459        let _ = CallbackQueue::queue_and_run_with_legacy(|cb_queue| self.close_helper(cb_queue));
460    }
461}
462
463/// A file descriptor that reference an open file. Also contains flags that change the behaviour of
464/// this file descriptor.
465#[derive(Debug, Clone)]
466pub struct Descriptor {
467    /// The file that this descriptor points to.
468    // `Option` wrapper so that our `Drop` implementation can ensure that the
469    // file was closed or removed before drop.
470    file: Option<CompatFile>,
471    /// Descriptor flags.
472    flags: DescriptorFlags,
473    _counter: ObjectCounter,
474}
475
476// will not compile if `Descriptor` is not Send + Sync
477impl IsSend for Descriptor {}
478impl IsSync for Descriptor {}
479
480impl Descriptor {
481    pub fn new(file: CompatFile) -> Self {
482        Self {
483            file: Some(file),
484            flags: DescriptorFlags::empty(),
485            _counter: ObjectCounter::new("Descriptor"),
486        }
487    }
488
489    pub fn file(&self) -> &CompatFile {
490        self.file.as_ref().unwrap()
491    }
492
493    pub fn flags(&self) -> DescriptorFlags {
494        self.flags
495    }
496
497    pub fn set_flags(&mut self, flags: DescriptorFlags) {
498        self.flags = flags;
499    }
500
501    pub fn into_file(mut self) -> CompatFile {
502        self.file.take().unwrap()
503    }
504
505    fn drop_posix_record_locks(&self, host: &Host, pid: ProcessId) {
506        let owner = super::fcntl_lock_table::LockOwner::Process(pid);
507        let stat = match self.file().stat() {
508            Ok(s) => s,
509            Err(e) => {
510                log::debug!("Couldn't stat file to drop record locks: {e:?}");
511                return;
512            }
513        };
514        let fid = FileId::from(&stat);
515        host.fcntl_lock_table_borrow_mut().remove_owner(fid, &owner);
516    }
517
518    /// Close the descriptor.
519    // Caller needs to tell us which PID to drop posix record locks for, if any.
520    // We can't store the "owning" PID in the `Descriptor`, nor the
521    // `DescriptorTable`, since both can be shared across multiple processes
522    // (e.g. after `clone` with `CLONE_FILES` and without `CLONE_THREAD`).
523    //
524    // We use the Host reference both for closing legacy files, and for
525    // accessing the record-lock table.
526    pub fn close(
527        self,
528        host: &Host,
529        drop_posix_record_locks: DropPosixRecordLocks,
530        cb_queue: &mut CallbackQueue,
531    ) -> Option<Result<(), SyscallError>> {
532        match drop_posix_record_locks {
533            DropPosixRecordLocks::False => (),
534            DropPosixRecordLocks::ForPid(pid) => self.drop_posix_record_locks(host, pid),
535        }
536        self.into_file().close(host, cb_queue)
537    }
538
539    /// Duplicate the descriptor, with both descriptors pointing to the same `OpenFile`. In
540    /// Linux, the descriptor flags aren't typically copied to the new descriptor, so we
541    /// explicitly require a flags value to avoid confusion.
542    pub fn dup(&self, flags: DescriptorFlags) -> Self {
543        Self {
544            file: self.file.clone(),
545            flags,
546            _counter: ObjectCounter::new("Descriptor"),
547        }
548    }
549
550    pub fn into_raw(descriptor: Box<Self>) -> *mut Self {
551        Box::into_raw(descriptor)
552    }
553
554    pub fn from_raw(descriptor: *mut Self) -> Option<Box<Self>> {
555        if descriptor.is_null() {
556            return None;
557        }
558
559        unsafe { Some(Box::from_raw(descriptor)) }
560    }
561
562    /// The new descriptor takes ownership of the reference to the legacy file and does not
563    /// increment its ref count, but will decrement the ref count when this descriptor is
564    /// freed/dropped with `descriptor_free()`. The descriptor flags must be either 0 or
565    /// `O_CLOEXEC`.
566    ///
567    /// If creating a descriptor for a `TCP` object, you should use `descriptor_fromLegacyTcp`
568    /// instead. If `legacy_file` is a TCP socket, this function will panic.
569    ///
570    /// # Safety
571    ///
572    /// Takes ownership of `legacy_file`, which must be safely dereferenceable.
573    pub unsafe fn from_legacy_file(
574        legacy_file: *mut c::LegacyFile,
575        descriptor_flags: OFlag,
576    ) -> Descriptor {
577        assert!(!legacy_file.is_null());
578
579        // if it's a TCP socket, `descriptor_fromLegacyTcp` should be used instead
580        assert_ne!(
581            unsafe { c::legacyfile_getType(legacy_file) },
582            c::_LegacyFileType_DT_TCPSOCKET,
583        );
584
585        let mut descriptor = Descriptor::new(CompatFile::Legacy(LegacyFileCounter::new(
586            CountedLegacyFileRef::new(HostTreePointer::new(legacy_file)),
587        )));
588
589        let (descriptor_flags, remaining) = DescriptorFlags::from_o_flags(descriptor_flags);
590        assert!(remaining.is_empty());
591        descriptor.set_flags(descriptor_flags);
592        descriptor
593    }
594}
595
596impl Drop for Descriptor {
597    fn drop(&mut self) {
598        if self.file.is_some() {
599            warn_and_debug_panic!("Dropped descriptor without closing");
600        }
601    }
602}
603
604impl ExplicitDrop for Descriptor {
605    type ExplicitDropParam<'p> = (&'p Host, DropPosixRecordLocks, &'p mut CallbackQueue);
606    type ExplicitDropResult = Option<Result<(), SyscallError>>;
607
608    fn explicit_drop<'p>(self, param: Self::ExplicitDropParam<'p>) -> Self::ExplicitDropResult {
609        let (host, drop_posix_record_locks, cb_queue) = param;
610        self.close(host, drop_posix_record_locks, cb_queue)
611    }
612}
613
614/// Represents a counted reference to a legacy file object. Will decrement the legacy file's ref
615/// count when dropped.
616#[derive(Debug)]
617pub struct CountedLegacyFileRef(HostTreePointer<c::LegacyFile>);
618
619impl CountedLegacyFileRef {
620    /// Does not increment the legacy file's ref count, but will decrement the ref count when
621    /// dropped.
622    pub fn new(ptr: HostTreePointer<c::LegacyFile>) -> Self {
623        Self(ptr)
624    }
625
626    /// # Safety
627    ///
628    /// See `HostTreePointer::ptr`.
629    pub unsafe fn ptr(&self) -> *mut c::LegacyFile {
630        unsafe { self.0.ptr() }
631    }
632}
633
634impl std::clone::Clone for CountedLegacyFileRef {
635    fn clone(&self) -> Self {
636        // ref the legacy file object
637        unsafe { c::legacyfile_ref(self.0.ptr() as *mut core::ffi::c_void) };
638        Self(self.0)
639    }
640}
641
642impl Drop for CountedLegacyFileRef {
643    fn drop(&mut self) {
644        // unref the legacy file object
645        unsafe { c::legacyfile_unref(self.0.ptr() as *mut core::ffi::c_void) };
646    }
647}
648
649/// Used to track how many descriptors are open for a [`LegacyFile`][c::LegacyFile].
650///
651/// When the `close()` method is called, the legacy file's `legacyfile_close()` will only be called
652/// if this is the last descriptor for that legacy file. This is similar to an [`OpenFile`] object,
653/// but for C files.
654#[derive(Clone, Debug)]
655pub struct LegacyFileCounter {
656    file: Option<CountedLegacyFileRef>,
657    /// A count of how many open descriptors there are with reference to this legacy file.
658    open_count: Arc<()>,
659}
660
661impl LegacyFileCounter {
662    pub fn new(file: CountedLegacyFileRef) -> Self {
663        Self {
664            file: Some(file),
665            open_count: Arc::new(()),
666        }
667    }
668
669    pub fn ptr(&self) -> *mut c::LegacyFile {
670        unsafe { self.file.as_ref().unwrap().ptr() }
671    }
672
673    pub fn lseek(
674        &self,
675        off: linux_api::posix_types::kernel_off_t,
676        whence: linux_api::unistd::LSeekWhence,
677    ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError> {
678        let rv = unsafe {
679            c::legacyfile_lseek(self.ptr(), off, i32::try_from(u32::from(whence)).unwrap())
680        };
681        if rv < 0 {
682            return Err(Errno::from_libc_errnum(-i32::try_from(rv).unwrap())
683                .unwrap()
684                .into());
685        }
686        Ok(rv)
687    }
688
689    pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
690        let mut statbuf: linux_api::stat::stat = shadow_pod::zeroed();
691        let rv = unsafe { c::legacyfile_fstat(self.ptr(), &mut statbuf) };
692        if rv < 0 {
693            return Err(Errno::from_libc_errnum(-rv).unwrap().into());
694        }
695        Ok(statbuf)
696    }
697
698    pub fn set_status(&self, status: FileStatus) {
699        unsafe { c::legacyfile_setFileStatusFlags(self.ptr(), status.bits()) }
700    }
701
702    pub fn status(&self) -> FileStatus {
703        let raw = unsafe { c::legacyfile_getFileStatusFlags(self.ptr()) };
704        FileStatus::from_bits_truncate(raw)
705    }
706
707    pub fn mode(&self) -> FileMode {
708        let raw_flags = unsafe { c::legacyfile_getAccessModeFlags(self.ptr()) };
709        let oflags = OFlag::from_bits_retain(raw_flags);
710        let (mode, _other_flags) =
711            FileMode::from_o_flags(oflags).expect("Invalid flags for open file");
712        mode
713    }
714
715    /// Should drop `self` immediately after calling this.
716    fn close_helper(&mut self, host: &Host) {
717        // Always take out the `file` object, so that our `Drop` impl knows this object
718        // was closed properly.
719        let Some(file) = self.file.take() else {
720            warn_and_debug_panic!("Tried to close missing file");
721            #[allow(unreachable_code)]
722            return;
723        };
724        // this isn't subject to race conditions since we should never access descriptors
725        // from multiple threads at the same time
726        if Arc::<()>::strong_count(&self.open_count) == 1 {
727            unsafe { c::legacyfile_close(file.ptr(), host) }
728        }
729    }
730
731    /// Close the descriptor, and if this is the last descriptor pointing to its legacy file, close
732    /// the legacy file as well.
733    pub fn close(mut self, host: &Host) {
734        self.close_helper(host);
735    }
736}
737
738impl std::ops::Drop for LegacyFileCounter {
739    fn drop(&mut self) {
740        if self.file.is_some() {
741            warn_and_debug_panic!("Dropped LegacyFileCounter without explicitly closing");
742            #[allow(unreachable_code)]
743            crate::core::worker::Worker::with_active_host(|host| self.close_helper(host)).unwrap();
744        }
745    }
746}
747
748impl ExplicitDrop for LegacyFileCounter {
749    type ExplicitDropParam<'p> = &'p Host;
750    type ExplicitDropResult = ();
751
752    fn explicit_drop<'p>(mut self, host: Self::ExplicitDropParam<'p>) -> Self::ExplicitDropResult {
753        self.close_helper(host);
754    }
755}
756
757/// A compatibility wrapper around an [`OpenFile`] or [`LegacyFileCounter`].
758#[derive(Clone, Debug)]
759pub enum CompatFile {
760    New(OpenFile),
761    Legacy(LegacyFileCounter),
762}
763
764impl CompatFile {
765    /// Close the file. The `host` option is a legacy option for legacy files.
766    pub fn close(
767        self,
768        host: &Host,
769        cb_queue: &mut CallbackQueue,
770    ) -> Option<Result<(), SyscallError>> {
771        match self {
772            Self::New(file) => file.close(cb_queue),
773            Self::Legacy(file) => {
774                file.close(host);
775                Some(Ok(()))
776            }
777        }
778    }
779
780    pub fn lseek(
781        &self,
782        off: linux_api::posix_types::kernel_off_t,
783        whence: linux_api::unistd::LSeekWhence,
784    ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError> {
785        match self {
786            CompatFile::New(file) => file.inner_file().borrow_mut().lseek(off, whence),
787            CompatFile::Legacy(file) => file.lseek(off, whence),
788        }
789    }
790
791    pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
792        match self {
793            CompatFile::New(open_file) => open_file.inner_file().borrow().stat(),
794            CompatFile::Legacy(legacy_file_counter) => Ok(legacy_file_counter.stat()?),
795        }
796    }
797
798    pub fn set_status(&self, status: FileStatus) {
799        match self {
800            CompatFile::New(open_file) => open_file.inner_file().borrow_mut().set_status(status),
801            CompatFile::Legacy(legacy_file_counter) => legacy_file_counter.set_status(status),
802        }
803    }
804
805    pub fn status(&self) -> FileStatus {
806        match self {
807            CompatFile::New(open_file) => open_file.inner_file().borrow().status(),
808            CompatFile::Legacy(legacy_file_counter) => legacy_file_counter.status(),
809        }
810    }
811
812    pub fn mode(&self) -> FileMode {
813        match self {
814            CompatFile::New(open_file) => open_file.inner_file().borrow().mode(),
815            CompatFile::Legacy(legacy_file_counter) => legacy_file_counter.mode(),
816        }
817    }
818}
819
820mod export {
821    use super::*;
822
823    use crate::host::descriptor::socket::inet::InetSocket;
824    use crate::host::descriptor::socket::inet::legacy_tcp::LegacyTcpSocket;
825
826    /// The new descriptor takes ownership of the reference to the legacy file and does not
827    /// increment its ref count, but will decrement the ref count when this descriptor is
828    /// freed/dropped with `descriptor_free()`. The descriptor flags must be either 0 or
829    /// `O_CLOEXEC`.
830    ///
831    /// If creating a descriptor for a `TCP` object, you should use `descriptor_fromLegacyTcp`
832    /// instead. If `legacy_file` is a TCP socket, this function will panic.
833    #[unsafe(no_mangle)]
834    pub unsafe extern "C-unwind" fn descriptor_fromLegacyFile(
835        legacy_file: *mut c::LegacyFile,
836        descriptor_flags: libc::c_int,
837    ) -> *mut Descriptor {
838        let descriptor_flags = OFlag::from_bits(descriptor_flags).unwrap();
839        let descriptor = unsafe { Descriptor::from_legacy_file(legacy_file, descriptor_flags) };
840        Descriptor::into_raw(Box::new(descriptor))
841    }
842
843    /// The new descriptor takes ownership of the reference to the legacy TCP object and does not
844    /// increment its ref count, but will decrement the ref count when this descriptor is
845    /// freed/dropped with `descriptor_free()`. The descriptor flags must be either 0 or
846    /// `O_CLOEXEC`.
847    #[unsafe(no_mangle)]
848    pub unsafe extern "C-unwind" fn descriptor_fromLegacyTcp(
849        legacy_tcp: *mut c::TCP,
850        descriptor_flags: libc::c_int,
851    ) -> *mut Descriptor {
852        assert!(!legacy_tcp.is_null());
853
854        let tcp = unsafe { LegacyTcpSocket::new_from_legacy(legacy_tcp) };
855        let mut descriptor = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(
856            Socket::Inet(InetSocket::LegacyTcp(tcp)),
857        ))));
858
859        let descriptor_flags = OFlag::from_bits(descriptor_flags).unwrap();
860        let (descriptor_flags, remaining) = DescriptorFlags::from_o_flags(descriptor_flags);
861        assert!(remaining.is_empty());
862        descriptor.set_flags(descriptor_flags);
863
864        Descriptor::into_raw(Box::new(descriptor))
865    }
866
867    /// If the descriptor is a legacy file, returns a pointer to the legacy file object. Otherwise
868    /// returns NULL. The legacy file's ref count is not modified, so the pointer must not outlive
869    /// the lifetime of the descriptor.
870    #[unsafe(no_mangle)]
871    pub extern "C-unwind" fn descriptor_asLegacyFile(
872        descriptor: *const Descriptor,
873    ) -> *mut c::LegacyFile {
874        assert!(!descriptor.is_null());
875
876        let descriptor = unsafe { &*descriptor };
877
878        if let CompatFile::Legacy(d) = descriptor.file() {
879            d.ptr()
880        } else {
881            std::ptr::null_mut()
882        }
883    }
884
885    /// If the descriptor is a new/rust descriptor, returns a pointer to the reference-counted
886    /// `OpenFile` object. Otherwise returns NULL. The `OpenFile` object's ref count is not
887    /// modified, so the returned pointer must not outlive the lifetime of the descriptor.
888    #[unsafe(no_mangle)]
889    pub extern "C-unwind" fn descriptor_borrowOpenFile(
890        descriptor: *const Descriptor,
891    ) -> *const OpenFile {
892        assert!(!descriptor.is_null());
893
894        let descriptor = unsafe { &*descriptor };
895
896        match descriptor.file() {
897            CompatFile::Legacy(_) => std::ptr::null_mut(),
898            CompatFile::New(d) => d,
899        }
900    }
901
902    /// If the descriptor is a new/rust descriptor, returns a pointer to the reference-counted
903    /// `OpenFile` object. Otherwise returns NULL. The `OpenFile` object's ref count is incremented,
904    /// so the returned pointer must always later be passed to `openfile_drop()`, otherwise the
905    /// memory will leak.
906    #[unsafe(no_mangle)]
907    pub extern "C-unwind" fn descriptor_newRefOpenFile(
908        descriptor: *const Descriptor,
909    ) -> *const OpenFile {
910        assert!(!descriptor.is_null());
911
912        let descriptor = unsafe { &*descriptor };
913
914        match descriptor.file() {
915            CompatFile::Legacy(_) => std::ptr::null_mut(),
916            CompatFile::New(d) => Box::into_raw(Box::new(d.clone())),
917        }
918    }
919
920    /// The descriptor flags must be either 0 or `O_CLOEXEC`.
921    #[unsafe(no_mangle)]
922    pub extern "C-unwind" fn descriptor_setFlags(descriptor: *mut Descriptor, flags: libc::c_int) {
923        assert!(!descriptor.is_null());
924
925        let descriptor = unsafe { &mut *descriptor };
926        let flags = OFlag::from_bits(flags).unwrap();
927        let (flags, remaining_flags) = DescriptorFlags::from_o_flags(flags);
928        assert!(remaining_flags.is_empty());
929
930        descriptor.set_flags(flags);
931    }
932
933    /// Decrement the ref count of the `OpenFile` object. The pointer must not be used after calling
934    /// this function.
935    #[unsafe(no_mangle)]
936    pub extern "C-unwind" fn openfile_drop(file: *const OpenFile) {
937        assert!(!file.is_null());
938
939        drop(unsafe { Box::from_raw(file.cast_mut()) });
940    }
941
942    /// Get the state of the `OpenFile` object.
943    #[unsafe(no_mangle)]
944    pub extern "C-unwind" fn openfile_getStatus(file: *const OpenFile) -> FileState {
945        assert!(!file.is_null());
946
947        let file = unsafe { &*file };
948
949        file.inner_file().borrow().state()
950    }
951
952    /// Add a status listener to the `OpenFile` object. This will increment the status listener's
953    /// ref count, and will decrement the ref count when this status listener is removed or when the
954    /// `OpenFile` is freed/dropped.
955    #[unsafe(no_mangle)]
956    pub unsafe extern "C-unwind" fn openfile_addListener(
957        file: *const OpenFile,
958        listener: *mut c::StatusListener,
959    ) {
960        assert!(!file.is_null());
961        assert!(!listener.is_null());
962
963        let file = unsafe { &*file };
964
965        file.inner_file()
966            .borrow_mut()
967            .add_legacy_listener(HostTreePointer::new(listener));
968    }
969
970    /// Remove a listener from the `OpenFile` object.
971    #[unsafe(no_mangle)]
972    pub extern "C-unwind" fn openfile_removeListener(
973        file: *const OpenFile,
974        listener: *mut c::StatusListener,
975    ) {
976        assert!(!file.is_null());
977        assert!(!listener.is_null());
978
979        let file = unsafe { &*file };
980
981        file.inner_file()
982            .borrow_mut()
983            .remove_legacy_listener(listener);
984    }
985
986    /// Get the canonical handle for an `OpenFile` object. Two `OpenFile` objects refer to the same
987    /// underlying data if their handles are equal.
988    #[unsafe(no_mangle)]
989    pub extern "C-unwind" fn openfile_getCanonicalHandle(file: *const OpenFile) -> libc::uintptr_t {
990        assert!(!file.is_null());
991
992        let file = unsafe { &*file };
993
994        file.inner_file().canonical_handle()
995    }
996
997    /// If the descriptor is a new/rust descriptor, returns a pointer to the reference-counted
998    /// `File` object. Otherwise returns NULL. The `File` object's ref count is incremented, so the
999    /// pointer must always later be passed to `file_drop()`, otherwise the memory will leak.
1000    #[unsafe(no_mangle)]
1001    pub extern "C-unwind" fn descriptor_newRefFile(descriptor: *const Descriptor) -> *const File {
1002        assert!(!descriptor.is_null());
1003
1004        let descriptor = unsafe { &*descriptor };
1005
1006        match descriptor.file() {
1007            CompatFile::Legacy(_) => std::ptr::null_mut(),
1008            CompatFile::New(d) => Box::into_raw(Box::new(d.inner_file().clone())),
1009        }
1010    }
1011
1012    /// Decrement the ref count of the `File` object. The pointer must not be used after calling
1013    /// this function.
1014    #[unsafe(no_mangle)]
1015    pub extern "C-unwind" fn file_drop(file: *const File) {
1016        assert!(!file.is_null());
1017
1018        drop(unsafe { Box::from_raw(file.cast_mut()) });
1019    }
1020
1021    /// Increment the ref count of the `File` object. The returned pointer will not be the same as
1022    /// the given pointer (they are distinct references), and they both must be dropped with
1023    /// `file_drop` separately later.
1024    #[unsafe(no_mangle)]
1025    pub extern "C-unwind" fn file_cloneRef(file: *const File) -> *const File {
1026        let file = unsafe { file.as_ref() }.unwrap();
1027        Box::into_raw(Box::new(file.clone()))
1028    }
1029
1030    /// Get the state of the `File` object.
1031    #[unsafe(no_mangle)]
1032    pub extern "C-unwind" fn file_getStatus(file: *const File) -> FileState {
1033        assert!(!file.is_null());
1034
1035        let file = unsafe { &*file };
1036
1037        file.borrow().state()
1038    }
1039
1040    /// Add a status listener to the `File` object. This will increment the status listener's ref
1041    /// count, and will decrement the ref count when this status listener is removed or when the
1042    /// `File` is freed/dropped.
1043    #[unsafe(no_mangle)]
1044    pub unsafe extern "C-unwind" fn file_addListener(
1045        file: *const File,
1046        listener: *mut c::StatusListener,
1047    ) {
1048        assert!(!file.is_null());
1049        assert!(!listener.is_null());
1050
1051        let file = unsafe { &*file };
1052
1053        file.borrow_mut()
1054            .add_legacy_listener(HostTreePointer::new(listener));
1055    }
1056
1057    /// Remove a listener from the `File` object.
1058    #[unsafe(no_mangle)]
1059    pub extern "C-unwind" fn file_removeListener(
1060        file: *const File,
1061        listener: *mut c::StatusListener,
1062    ) {
1063        assert!(!file.is_null());
1064        assert!(!listener.is_null());
1065
1066        let file = unsafe { &*file };
1067
1068        file.borrow_mut().remove_legacy_listener(listener);
1069    }
1070
1071    /// Get the canonical handle for a `File` object. Two `File` objects refer to the same
1072    /// underlying data if their handles are equal.
1073    #[unsafe(no_mangle)]
1074    pub extern "C-unwind" fn file_getCanonicalHandle(file: *const File) -> libc::uintptr_t {
1075        assert!(!file.is_null());
1076
1077        let file = unsafe { &*file };
1078
1079        file.canonical_handle()
1080    }
1081}
1082
1083#[cfg(test)]
1084mod tests {
1085    use super::*;
1086    use crate::host::syscall::Trigger;
1087    use crate::host::syscall::condition::SyscallCondition;
1088    use crate::host::syscall::types::{
1089        Blocked, Failed, SyscallError, SyscallReturn, SyscallReturnBlocked, SyscallReturnDone,
1090    };
1091
1092    #[test]
1093    // can't call foreign function: syscallcondition_new
1094    #[cfg_attr(miri, ignore)]
1095    fn test_syscallresult_roundtrip() {
1096        for val in vec![
1097            Ok(1.into()),
1098            Err(linux_api::errno::Errno::EPERM.into()),
1099            Err(SyscallError::Failed(Failed {
1100                errno: linux_api::errno::Errno::EINTR,
1101                restartable: true,
1102            })),
1103            Err(SyscallError::Failed(Failed {
1104                errno: linux_api::errno::Errno::EINTR,
1105                restartable: false,
1106            })),
1107            Err(SyscallError::Blocked(Blocked {
1108                condition: SyscallCondition::new(Trigger::from(c::Trigger {
1109                    type_: 1,
1110                    object: c::TriggerObject {
1111                        as_pointer: std::ptr::null_mut(),
1112                    },
1113                    state: FileState::CLOSED,
1114                })),
1115                restartable: true,
1116            })),
1117        ]
1118        .drain(..)
1119        {
1120            // We can't easily compare the value to the roundtripped result, since
1121            // roundtripping consumes the original value, and SyscallReturn doesn't implement Clone.
1122            // Compare their debug strings instead.
1123            let orig_debug = format!("{:?}", &val);
1124            let roundtripped = SyscallResult::from(SyscallReturn::from(val));
1125            let roundtripped_debug = format!("{roundtripped:?}");
1126            assert_eq!(orig_debug, roundtripped_debug);
1127        }
1128    }
1129
1130    #[test]
1131    // can't call foreign function: syscallcondition_new
1132    #[cfg_attr(miri, ignore)]
1133    fn test_syscallreturn_roundtrip() {
1134        let condition = SyscallCondition::new(Trigger::from(c::Trigger {
1135            type_: 1,
1136            object: c::TriggerObject {
1137                as_pointer: std::ptr::null_mut(),
1138            },
1139            state: FileState::CLOSED,
1140        }));
1141        for val in vec![
1142            SyscallReturn::Done(SyscallReturnDone {
1143                retval: 1.into(),
1144                restartable: false,
1145            }),
1146            SyscallReturn::Block(SyscallReturnBlocked {
1147                cond: condition.into_inner(),
1148                restartable: true,
1149            }),
1150            SyscallReturn::Native,
1151        ]
1152        .drain(..)
1153        {
1154            // We can't easily compare the value to the roundtripped result,
1155            // since roundtripping consumes the original value, and
1156            // SyscallReturn doesn't implement Clone. Compare their debug
1157            // strings instead.
1158            let orig_debug = format!("{:?}", &val);
1159            let roundtripped = SyscallReturn::from(SyscallResult::from(val));
1160            let roundtripped_debug = format!("{roundtripped:?}");
1161            assert_eq!(orig_debug, roundtripped_debug);
1162        }
1163    }
1164
1165    #[test]
1166    fn test_file_mode_o_flags() {
1167        // test from O flags to FileMode
1168        assert_eq!(
1169            FileMode::from_o_flags(OFlag::O_PATH),
1170            Ok((FileMode::empty(), OFlag::empty()))
1171        );
1172        assert_eq!(
1173            FileMode::from_o_flags(OFlag::O_WRONLY),
1174            Ok((FileMode::WRITE, OFlag::empty()))
1175        );
1176        assert_eq!(
1177            FileMode::from_o_flags(OFlag::O_RDWR),
1178            Ok((FileMode::READ | FileMode::WRITE, OFlag::empty()))
1179        );
1180        assert_eq!(
1181            FileMode::from_o_flags(OFlag::O_RDONLY),
1182            Ok((FileMode::READ, OFlag::empty()))
1183        );
1184        assert_eq!(
1185            FileMode::from_o_flags(OFlag::empty()),
1186            Ok((FileMode::READ, OFlag::empty()))
1187        );
1188        assert_eq!(
1189            FileMode::from_o_flags(OFlag::O_RDWR | OFlag::O_WRONLY),
1190            Err(())
1191        );
1192        assert_eq!(
1193            FileMode::from_o_flags(OFlag::O_RDWR | OFlag::O_RDONLY),
1194            Ok((FileMode::READ | FileMode::WRITE, OFlag::empty()))
1195        );
1196        assert_eq!(
1197            FileMode::from_o_flags(OFlag::O_WRONLY | OFlag::O_RDONLY),
1198            Ok((FileMode::WRITE, OFlag::empty()))
1199        );
1200        assert_eq!(
1201            FileMode::from_o_flags(OFlag::O_PATH | OFlag::O_WRONLY),
1202            Err(())
1203        );
1204        assert_eq!(
1205            FileMode::from_o_flags(OFlag::O_WRONLY | OFlag::O_CLOEXEC),
1206            Ok((FileMode::WRITE, OFlag::O_CLOEXEC))
1207        );
1208
1209        // test from FileMode to O flags
1210        assert_eq!(FileMode::as_o_flags(&FileMode::empty()), OFlag::O_PATH);
1211        assert_eq!(FileMode::as_o_flags(&FileMode::READ), OFlag::O_RDONLY);
1212        assert_eq!(FileMode::as_o_flags(&FileMode::WRITE), OFlag::O_WRONLY);
1213        assert_eq!(
1214            FileMode::as_o_flags(&(FileMode::READ | FileMode::WRITE)),
1215            OFlag::O_RDWR
1216        );
1217    }
1218}