1use std::sync::Arc;
4
5use atomic_refcell::AtomicRefCell;
6use linux_api::errno::Errno;
7use linux_api::fcntl::{DescriptorFlags, OFlag};
8use linux_api::ioctls::IoctlRequest;
9use shadow_shim_helper_rs::syscall_types::ForeignPtr;
10
11use crate::core::worker;
12use crate::cshadow as c;
13use crate::host::descriptor::listener::{StateListenHandle, StateListenerFilter};
14use crate::host::descriptor::socket::{Socket, SocketRef, SocketRefMut};
15use crate::host::host::Host;
16use crate::host::memory_manager::MemoryManager;
17use crate::host::syscall::io::IoVec;
18use crate::host::syscall::types::{SyscallError, SyscallResult};
19use crate::utility::callback_queue::CallbackQueue;
20use crate::utility::{HostTreePointer, IsSend, IsSync, ObjectCounter};
21use shadow_shim_helper_rs::explicit_drop::ExplicitDrop;
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
32bitflags::bitflags! {
33 #[derive(Copy, Clone, Debug)]
40 pub struct FileStatus: i32 {
41 const NONBLOCK = OFlag::O_NONBLOCK.bits();
42 const APPEND = OFlag::O_APPEND.bits();
43 const ASYNC = OFlag::O_ASYNC.bits();
44 const DIRECT = OFlag::O_DIRECT.bits();
45 const NOATIME = OFlag::O_NOATIME.bits();
46 }
47}
48
49impl FileStatus {
50 pub fn as_o_flags(&self) -> OFlag {
51 OFlag::from_bits(self.bits()).unwrap()
52 }
53
54 pub fn from_o_flags(flags: OFlag) -> (Self, OFlag) {
56 let status = Self::from_bits_truncate(flags.bits());
57 let remaining = flags.bits() & !status.bits();
58 (status, OFlag::from_bits(remaining).unwrap())
59 }
60}
61
62bitflags::bitflags! {
63 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
69 pub struct FileMode: u32 {
70 const READ = 0b00000001;
71 const WRITE = 0b00000010;
72 }
73}
74
75impl FileMode {
76 pub fn as_o_flags(&self) -> OFlag {
77 const READ_AND_WRITE: FileMode = FileMode::READ.union(FileMode::WRITE);
78 const EMPTY: FileMode = FileMode::empty();
79
80 match *self {
82 READ_AND_WRITE => OFlag::O_RDWR,
83 Self::READ => OFlag::O_RDONLY,
84 Self::WRITE => OFlag::O_WRONLY,
85 EMPTY => OFlag::O_PATH,
87 _ => panic!("Invalid file mode flags"),
88 }
89 }
90
91 #[allow(clippy::result_unit_err)]
94 pub fn from_o_flags(flags: OFlag) -> Result<(Self, OFlag), ()> {
95 let mode = flags & (OFlag::O_ACCMODE | OFlag::O_PATH);
98 let remaining = flags - (OFlag::O_ACCMODE | OFlag::O_PATH);
99
100 let mode = match mode {
102 OFlag::O_RDONLY => FileMode::READ,
103 OFlag::O_WRONLY => FileMode::WRITE,
104 OFlag::O_RDWR => FileMode::READ | FileMode::WRITE,
105 OFlag::O_PATH => FileMode::empty(),
106 _ => return Err(()),
107 };
108
109 Ok((mode, remaining))
110 }
111}
112
113bitflags::bitflags! {
114 #[derive(Default, Copy, Clone, Debug)]
124 #[repr(transparent)]
125 pub struct FileState: u16 {
126 #[deprecated(note = "use `FileState::empty()`")]
128 const NONE = 0;
129 const ACTIVE = 1 << 0;
134 const READABLE = 1 << 1;
136 const WRITABLE = 1 << 2;
138 const CLOSED = 1 << 3;
140 const FUTEX_WAKEUP = 1 << 4;
142 const CHILD_EVENT = 1 << 5;
144 const SOCKET_ALLOWING_CONNECT = 1 << 6;
147 const RDHUP = 1 << 7;
150 }
151}
152
153bitflags::bitflags! {
154 #[derive(Default, Copy, Clone, Debug)]
156 #[repr(transparent)]
157 pub struct FileSignals: u32 {
158 const READ_BUFFER_GREW = 1 << 0;
160 }
161}
162
163#[derive(Clone)]
165pub enum File {
166 Pipe(Arc<AtomicRefCell<pipe::Pipe>>),
167 EventFd(Arc<AtomicRefCell<eventfd::EventFd>>),
168 Socket(Socket),
169 TimerFd(Arc<AtomicRefCell<timerfd::TimerFd>>),
170 Epoll(Arc<AtomicRefCell<epoll::Epoll>>),
171}
172
173impl IsSend for File {}
175impl IsSync for File {}
176
177impl File {
178 pub fn borrow(&self) -> FileRef<'_> {
179 match self {
180 Self::Pipe(f) => FileRef::Pipe(f.borrow()),
181 Self::EventFd(f) => FileRef::EventFd(f.borrow()),
182 Self::Socket(f) => FileRef::Socket(f.borrow()),
183 Self::TimerFd(f) => FileRef::TimerFd(f.borrow()),
184 Self::Epoll(f) => FileRef::Epoll(f.borrow()),
185 }
186 }
187
188 pub fn try_borrow(&self) -> Result<FileRef<'_>, atomic_refcell::BorrowError> {
189 Ok(match self {
190 Self::Pipe(f) => FileRef::Pipe(f.try_borrow()?),
191 Self::EventFd(f) => FileRef::EventFd(f.try_borrow()?),
192 Self::Socket(f) => FileRef::Socket(f.try_borrow()?),
193 Self::TimerFd(f) => FileRef::TimerFd(f.try_borrow()?),
194 Self::Epoll(f) => FileRef::Epoll(f.try_borrow()?),
195 })
196 }
197
198 pub fn borrow_mut(&self) -> FileRefMut<'_> {
199 match self {
200 Self::Pipe(f) => FileRefMut::Pipe(f.borrow_mut()),
201 Self::EventFd(f) => FileRefMut::EventFd(f.borrow_mut()),
202 Self::Socket(f) => FileRefMut::Socket(f.borrow_mut()),
203 Self::TimerFd(f) => FileRefMut::TimerFd(f.borrow_mut()),
204 Self::Epoll(f) => FileRefMut::Epoll(f.borrow_mut()),
205 }
206 }
207
208 pub fn try_borrow_mut(&self) -> Result<FileRefMut<'_>, atomic_refcell::BorrowMutError> {
209 Ok(match self {
210 Self::Pipe(f) => FileRefMut::Pipe(f.try_borrow_mut()?),
211 Self::EventFd(f) => FileRefMut::EventFd(f.try_borrow_mut()?),
212 Self::Socket(f) => FileRefMut::Socket(f.try_borrow_mut()?),
213 Self::TimerFd(f) => FileRefMut::TimerFd(f.try_borrow_mut()?),
214 Self::Epoll(f) => FileRefMut::Epoll(f.try_borrow_mut()?),
215 })
216 }
217
218 pub fn canonical_handle(&self) -> usize {
219 match self {
220 Self::Pipe(f) => Arc::as_ptr(f) as usize,
221 Self::EventFd(f) => Arc::as_ptr(f) as usize,
222 Self::Socket(f) => f.canonical_handle(),
223 Self::TimerFd(f) => Arc::as_ptr(f) as usize,
224 Self::Epoll(f) => Arc::as_ptr(f) as usize,
225 }
226 }
227}
228
229impl std::fmt::Debug for File {
230 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231 match self {
232 Self::Pipe(_) => write!(f, "Pipe")?,
233 Self::EventFd(_) => write!(f, "EventFd")?,
234 Self::Socket(_) => write!(f, "Socket")?,
235 Self::TimerFd(_) => write!(f, "TimerFd")?,
236 Self::Epoll(_) => write!(f, "Epoll")?,
237 }
238
239 if let Ok(file) = self.try_borrow() {
240 let state = file.state();
241 let status = file.status();
242 write!(f, "(state: {state:?}, status: {status:?})")
243 } else {
244 write!(f, "(already borrowed)")
245 }
246 }
247}
248
249pub enum FileRef<'a> {
251 Pipe(atomic_refcell::AtomicRef<'a, pipe::Pipe>),
252 EventFd(atomic_refcell::AtomicRef<'a, eventfd::EventFd>),
253 Socket(SocketRef<'a>),
254 TimerFd(atomic_refcell::AtomicRef<'a, timerfd::TimerFd>),
255 Epoll(atomic_refcell::AtomicRef<'a, epoll::Epoll>),
256}
257
258pub enum FileRefMut<'a> {
261 Pipe(atomic_refcell::AtomicRefMut<'a, pipe::Pipe>),
262 EventFd(atomic_refcell::AtomicRefMut<'a, eventfd::EventFd>),
263 Socket(SocketRefMut<'a>),
264 TimerFd(atomic_refcell::AtomicRefMut<'a, timerfd::TimerFd>),
265 Epoll(atomic_refcell::AtomicRefMut<'a, epoll::Epoll>),
266}
267
268impl FileRef<'_> {
269 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
270 pub fn state(&self) -> FileState
271 );
272 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
273 pub fn mode(&self) -> FileMode
274 );
275 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
276 pub fn status(&self) -> FileStatus
277 );
278 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
279 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError>
280 );
281 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
282 pub fn has_open_file(&self) -> bool
283 );
284 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
285 pub fn supports_sa_restart(&self) -> bool
286 );
287}
288
289impl FileRefMut<'_> {
290 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
291 pub fn state(&self) -> FileState
292 );
293 enum_passthrough!(self, (off, whence), Pipe, EventFd, Socket, TimerFd, Epoll;
294 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>
295 );
296 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
297 pub fn mode(&self) -> FileMode
298 );
299 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
300 pub fn status(&self) -> FileStatus
301 );
302 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
303 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError>
304 );
305 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
306 pub fn has_open_file(&self) -> bool
307 );
308 enum_passthrough!(self, (), Pipe, EventFd, Socket, TimerFd, Epoll;
309 pub fn supports_sa_restart(&self) -> bool
310 );
311 enum_passthrough!(self, (val), Pipe, EventFd, Socket, TimerFd, Epoll;
312 pub fn set_has_open_file(&mut self, val: bool)
313 );
314 enum_passthrough!(self, (cb_queue), Pipe, EventFd, Socket, TimerFd, Epoll;
315 pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError>
316 );
317 enum_passthrough!(self, (status), Pipe, EventFd, Socket, TimerFd, Epoll;
318 pub fn set_status(&mut self, status: FileStatus)
319 );
320 enum_passthrough!(self, (request, arg_ptr, memory_manager), Pipe, EventFd, Socket, TimerFd, Epoll;
321 pub fn ioctl(&mut self, request: IoctlRequest, arg_ptr: ForeignPtr<()>, memory_manager: &mut MemoryManager) -> SyscallResult
322 );
323 enum_passthrough!(self, (monitoring_state, monitoring_signals, filter, notify_fn), Pipe, EventFd, Socket, TimerFd, Epoll;
324 pub fn add_listener(
325 &mut self,
326 monitoring_state: FileState,
327 monitoring_signals: FileSignals,
328 filter: StateListenerFilter,
329 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue) + Send + Sync + 'static,
330 ) -> StateListenHandle
331 );
332 enum_passthrough!(self, (ptr), Pipe, EventFd, Socket, TimerFd, Epoll;
333 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>)
334 );
335 enum_passthrough!(self, (ptr), Pipe, EventFd, Socket, TimerFd, Epoll;
336 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener)
337 );
338 enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Pipe, EventFd, Socket, TimerFd, Epoll;
339 pub fn readv(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
340 mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
341 );
342 enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Pipe, EventFd, Socket, TimerFd, Epoll;
343 pub fn writev(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
344 mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
345 );
346}
347
348impl std::fmt::Debug for FileRef<'_> {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 match self {
351 Self::Pipe(_) => write!(f, "Pipe")?,
352 Self::EventFd(_) => write!(f, "EventFd")?,
353 Self::Socket(_) => write!(f, "Socket")?,
354 Self::TimerFd(_) => write!(f, "TimerFd")?,
355 Self::Epoll(_) => write!(f, "Epoll")?,
356 }
357
358 let state = self.state();
359 let status = self.status();
360 write!(f, "(state: {state:?}, status: {status:?})")
361 }
362}
363
364impl std::fmt::Debug for FileRefMut<'_> {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 match self {
367 Self::Pipe(_) => write!(f, "Pipe")?,
368 Self::EventFd(_) => write!(f, "EventFd")?,
369 Self::Socket(_) => write!(f, "Socket")?,
370 Self::TimerFd(_) => write!(f, "TimerFd")?,
371 Self::Epoll(_) => write!(f, "Epoll")?,
372 }
373
374 let state = self.state();
375 let status = self.status();
376 write!(f, "(state: {state:?}, status: {status:?})")
377 }
378}
379
380#[derive(Clone, Debug)]
395pub struct OpenFile {
396 inner: Arc<OpenFileInner>,
397 _counter: ObjectCounter,
398}
399
400impl IsSend for OpenFile {}
402impl IsSync for OpenFile {}
403
404impl OpenFile {
405 pub fn new(file: File) -> Self {
406 {
407 let mut file = file.borrow_mut();
408
409 if file.state().contains(FileState::CLOSED) {
410 warn_and_debug_panic!("Creating an `OpenFile` object for a closed file");
412 }
413
414 if file.has_open_file() {
415 warn_and_debug_panic!(
417 "Creating an `OpenFile` object for a file that already has an `OpenFile` object"
418 );
419 }
420
421 file.set_has_open_file(true);
422 }
423
424 Self {
425 inner: Arc::new(OpenFileInner::new(file)),
426 _counter: ObjectCounter::new("OpenFile"),
427 }
428 }
429
430 pub fn inner_file(&self) -> &File {
431 self.inner.file.as_ref().unwrap()
432 }
433
434 pub fn close(self, cb_queue: &mut CallbackQueue) -> Option<Result<(), SyscallError>> {
438 let OpenFile { inner, _counter } = self;
439
440 Arc::into_inner(inner).map(|inner| inner.close(cb_queue))
442 }
443}
444
445#[derive(Clone, Debug)]
446struct OpenFileInner {
447 file: Option<File>,
448 _counter: ObjectCounter,
449}
450
451impl OpenFileInner {
452 pub fn new(file: File) -> Self {
453 Self {
454 file: Some(file),
455 _counter: ObjectCounter::new("OpenFileInner"),
456 }
457 }
458
459 pub fn close(mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
460 self.close_helper(cb_queue)
461 }
462
463 fn close_helper(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
464 if let Some(file) = self.file.take() {
465 file.borrow_mut().close(cb_queue)?;
466 }
467 Ok(())
468 }
469}
470
471impl std::ops::Drop for OpenFileInner {
472 fn drop(&mut self) {
473 let _ = CallbackQueue::queue_and_run_with_legacy(|cb_queue| self.close_helper(cb_queue));
475 }
476}
477
478#[derive(Debug, Clone)]
481pub struct Descriptor {
482 file: Option<CompatFile>,
486 flags: DescriptorFlags,
488 _counter: ObjectCounter,
489}
490
491impl IsSend for Descriptor {}
493impl IsSync for Descriptor {}
494
495impl Descriptor {
496 pub fn new(file: CompatFile) -> Self {
497 Self {
498 file: Some(file),
499 flags: DescriptorFlags::empty(),
500 _counter: ObjectCounter::new("Descriptor"),
501 }
502 }
503
504 pub fn file(&self) -> &CompatFile {
505 self.file.as_ref().unwrap()
506 }
507
508 pub fn flags(&self) -> DescriptorFlags {
509 self.flags
510 }
511
512 pub fn set_flags(&mut self, flags: DescriptorFlags) {
513 self.flags = flags;
514 }
515
516 pub fn into_file(mut self) -> CompatFile {
517 self.file.take().unwrap()
518 }
519
520 pub fn close(
522 self,
523 host: &Host,
524 cb_queue: &mut CallbackQueue,
525 ) -> Option<Result<(), SyscallError>> {
526 self.into_file().close(host, cb_queue)
527 }
528
529 pub fn dup(&self, flags: DescriptorFlags) -> Self {
533 Self {
534 file: self.file.clone(),
535 flags,
536 _counter: ObjectCounter::new("Descriptor"),
537 }
538 }
539
540 pub fn into_raw(descriptor: Box<Self>) -> *mut Self {
541 Box::into_raw(descriptor)
542 }
543
544 pub fn from_raw(descriptor: *mut Self) -> Option<Box<Self>> {
545 if descriptor.is_null() {
546 return None;
547 }
548
549 unsafe { Some(Box::from_raw(descriptor)) }
550 }
551
552 pub unsafe fn from_legacy_file(
564 legacy_file: *mut c::LegacyFile,
565 descriptor_flags: OFlag,
566 ) -> Descriptor {
567 assert!(!legacy_file.is_null());
568
569 assert_ne!(
571 unsafe { c::legacyfile_getType(legacy_file) },
572 c::_LegacyFileType_DT_TCPSOCKET,
573 );
574
575 let mut descriptor = Descriptor::new(CompatFile::Legacy(LegacyFileCounter::new(
576 CountedLegacyFileRef::new(HostTreePointer::new(legacy_file)),
577 )));
578
579 let (descriptor_flags, remaining) = DescriptorFlags::from_o_flags(descriptor_flags);
580 assert!(remaining.is_empty());
581 descriptor.set_flags(descriptor_flags);
582 descriptor
583 }
584}
585
586impl Drop for Descriptor {
587 fn drop(&mut self) {
588 if self.file.is_some() {
589 warn_and_debug_panic!("Dropped descriptor without closing");
590 }
591 }
592}
593
594impl ExplicitDrop for Descriptor {
595 type ExplicitDropParam<'p> = (&'p Host, &'p mut CallbackQueue);
596 type ExplicitDropResult = Option<Result<(), SyscallError>>;
597
598 fn explicit_drop<'p>(self, param: Self::ExplicitDropParam<'p>) -> Self::ExplicitDropResult {
599 let (host, cb_queue) = param;
600 self.close(host, cb_queue)
601 }
602}
603
604#[derive(Debug)]
607pub struct CountedLegacyFileRef(HostTreePointer<c::LegacyFile>);
608
609impl CountedLegacyFileRef {
610 pub fn new(ptr: HostTreePointer<c::LegacyFile>) -> Self {
613 Self(ptr)
614 }
615
616 pub unsafe fn ptr(&self) -> *mut c::LegacyFile {
620 unsafe { self.0.ptr() }
621 }
622}
623
624impl std::clone::Clone for CountedLegacyFileRef {
625 fn clone(&self) -> Self {
626 unsafe { c::legacyfile_ref(self.0.ptr() as *mut core::ffi::c_void) };
628 Self(self.0)
629 }
630}
631
632impl Drop for CountedLegacyFileRef {
633 fn drop(&mut self) {
634 unsafe { c::legacyfile_unref(self.0.ptr() as *mut core::ffi::c_void) };
636 }
637}
638
639#[derive(Clone, Debug)]
645pub struct LegacyFileCounter {
646 file: Option<CountedLegacyFileRef>,
647 open_count: Arc<()>,
649}
650
651impl LegacyFileCounter {
652 pub fn new(file: CountedLegacyFileRef) -> Self {
653 Self {
654 file: Some(file),
655 open_count: Arc::new(()),
656 }
657 }
658
659 pub fn ptr(&self) -> *mut c::LegacyFile {
660 unsafe { self.file.as_ref().unwrap().ptr() }
661 }
662
663 pub fn lseek(
664 &self,
665 off: linux_api::posix_types::kernel_off_t,
666 whence: linux_api::unistd::LSeekWhence,
667 ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError> {
668 let rv = unsafe {
669 c::legacyfile_lseek(self.ptr(), off, i32::try_from(u32::from(whence)).unwrap())
670 };
671 if rv < 0 {
672 return Err(Errno::from_libc_errnum(-i32::try_from(rv).unwrap())
673 .unwrap()
674 .into());
675 }
676 Ok(rv)
677 }
678
679 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
680 let mut statbuf: linux_api::stat::stat = shadow_pod::zeroed();
681 let rv = unsafe { c::legacyfile_fstat(self.ptr(), &mut statbuf) };
682 if rv < 0 {
683 return Err(Errno::from_libc_errnum(-rv).unwrap().into());
684 }
685 Ok(statbuf)
686 }
687
688 fn close_helper(&mut self, host: &Host) {
690 if Arc::<()>::strong_count(&self.open_count) == 1
693 && let Some(file) = self.file.take()
694 {
695 unsafe { c::legacyfile_close(file.ptr(), host) }
696 }
697 }
698
699 pub fn close(mut self, host: &Host) {
702 self.close_helper(host);
703 }
704}
705
706impl std::ops::Drop for LegacyFileCounter {
707 fn drop(&mut self) {
708 worker::Worker::with_active_host(|host| self.close_helper(host)).unwrap();
709 }
710}
711
712#[derive(Clone, Debug)]
714pub enum CompatFile {
715 New(OpenFile),
716 Legacy(LegacyFileCounter),
717}
718
719impl CompatFile {
720 pub fn close(
722 self,
723 host: &Host,
724 cb_queue: &mut CallbackQueue,
725 ) -> Option<Result<(), SyscallError>> {
726 match self {
727 Self::New(file) => file.close(cb_queue),
728 Self::Legacy(file) => {
729 file.close(host);
730 Some(Ok(()))
731 }
732 }
733 }
734
735 pub fn lseek(
736 &self,
737 off: linux_api::posix_types::kernel_off_t,
738 whence: linux_api::unistd::LSeekWhence,
739 ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError> {
740 match self {
741 CompatFile::New(file) => file.inner_file().borrow_mut().lseek(off, whence),
742 CompatFile::Legacy(file) => file.lseek(off, whence),
743 }
744 }
745
746 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
747 match self {
748 CompatFile::New(open_file) => open_file.inner_file().borrow().stat(),
749 CompatFile::Legacy(legacy_file_counter) => Ok(legacy_file_counter.stat()?),
750 }
751 }
752}
753
754mod export {
755 use super::*;
756
757 use crate::host::descriptor::socket::inet::InetSocket;
758 use crate::host::descriptor::socket::inet::legacy_tcp::LegacyTcpSocket;
759
760 #[unsafe(no_mangle)]
768 pub unsafe extern "C-unwind" fn descriptor_fromLegacyFile(
769 legacy_file: *mut c::LegacyFile,
770 descriptor_flags: libc::c_int,
771 ) -> *mut Descriptor {
772 let descriptor_flags = OFlag::from_bits(descriptor_flags).unwrap();
773 let descriptor = unsafe { Descriptor::from_legacy_file(legacy_file, descriptor_flags) };
774 Descriptor::into_raw(Box::new(descriptor))
775 }
776
777 #[unsafe(no_mangle)]
782 pub unsafe extern "C-unwind" fn descriptor_fromLegacyTcp(
783 legacy_tcp: *mut c::TCP,
784 descriptor_flags: libc::c_int,
785 ) -> *mut Descriptor {
786 assert!(!legacy_tcp.is_null());
787
788 let tcp = unsafe { LegacyTcpSocket::new_from_legacy(legacy_tcp) };
789 let mut descriptor = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(
790 Socket::Inet(InetSocket::LegacyTcp(tcp)),
791 ))));
792
793 let descriptor_flags = OFlag::from_bits(descriptor_flags).unwrap();
794 let (descriptor_flags, remaining) = DescriptorFlags::from_o_flags(descriptor_flags);
795 assert!(remaining.is_empty());
796 descriptor.set_flags(descriptor_flags);
797
798 Descriptor::into_raw(Box::new(descriptor))
799 }
800
801 #[unsafe(no_mangle)]
805 pub extern "C-unwind" fn descriptor_asLegacyFile(
806 descriptor: *const Descriptor,
807 ) -> *mut c::LegacyFile {
808 assert!(!descriptor.is_null());
809
810 let descriptor = unsafe { &*descriptor };
811
812 if let CompatFile::Legacy(d) = descriptor.file() {
813 d.ptr()
814 } else {
815 std::ptr::null_mut()
816 }
817 }
818
819 #[unsafe(no_mangle)]
823 pub extern "C-unwind" fn descriptor_borrowOpenFile(
824 descriptor: *const Descriptor,
825 ) -> *const OpenFile {
826 assert!(!descriptor.is_null());
827
828 let descriptor = unsafe { &*descriptor };
829
830 match descriptor.file() {
831 CompatFile::Legacy(_) => std::ptr::null_mut(),
832 CompatFile::New(d) => d,
833 }
834 }
835
836 #[unsafe(no_mangle)]
841 pub extern "C-unwind" fn descriptor_newRefOpenFile(
842 descriptor: *const Descriptor,
843 ) -> *const OpenFile {
844 assert!(!descriptor.is_null());
845
846 let descriptor = unsafe { &*descriptor };
847
848 match descriptor.file() {
849 CompatFile::Legacy(_) => std::ptr::null_mut(),
850 CompatFile::New(d) => Box::into_raw(Box::new(d.clone())),
851 }
852 }
853
854 #[unsafe(no_mangle)]
856 pub extern "C-unwind" fn descriptor_setFlags(descriptor: *mut Descriptor, flags: libc::c_int) {
857 assert!(!descriptor.is_null());
858
859 let descriptor = unsafe { &mut *descriptor };
860 let flags = OFlag::from_bits(flags).unwrap();
861 let (flags, remaining_flags) = DescriptorFlags::from_o_flags(flags);
862 assert!(remaining_flags.is_empty());
863
864 descriptor.set_flags(flags);
865 }
866
867 #[unsafe(no_mangle)]
870 pub extern "C-unwind" fn openfile_drop(file: *const OpenFile) {
871 assert!(!file.is_null());
872
873 drop(unsafe { Box::from_raw(file.cast_mut()) });
874 }
875
876 #[unsafe(no_mangle)]
878 pub extern "C-unwind" fn openfile_getStatus(file: *const OpenFile) -> FileState {
879 assert!(!file.is_null());
880
881 let file = unsafe { &*file };
882
883 file.inner_file().borrow().state()
884 }
885
886 #[unsafe(no_mangle)]
890 pub unsafe extern "C-unwind" fn openfile_addListener(
891 file: *const OpenFile,
892 listener: *mut c::StatusListener,
893 ) {
894 assert!(!file.is_null());
895 assert!(!listener.is_null());
896
897 let file = unsafe { &*file };
898
899 file.inner_file()
900 .borrow_mut()
901 .add_legacy_listener(HostTreePointer::new(listener));
902 }
903
904 #[unsafe(no_mangle)]
906 pub extern "C-unwind" fn openfile_removeListener(
907 file: *const OpenFile,
908 listener: *mut c::StatusListener,
909 ) {
910 assert!(!file.is_null());
911 assert!(!listener.is_null());
912
913 let file = unsafe { &*file };
914
915 file.inner_file()
916 .borrow_mut()
917 .remove_legacy_listener(listener);
918 }
919
920 #[unsafe(no_mangle)]
923 pub extern "C-unwind" fn openfile_getCanonicalHandle(file: *const OpenFile) -> libc::uintptr_t {
924 assert!(!file.is_null());
925
926 let file = unsafe { &*file };
927
928 file.inner_file().canonical_handle()
929 }
930
931 #[unsafe(no_mangle)]
935 pub extern "C-unwind" fn descriptor_newRefFile(descriptor: *const Descriptor) -> *const File {
936 assert!(!descriptor.is_null());
937
938 let descriptor = unsafe { &*descriptor };
939
940 match descriptor.file() {
941 CompatFile::Legacy(_) => std::ptr::null_mut(),
942 CompatFile::New(d) => Box::into_raw(Box::new(d.inner_file().clone())),
943 }
944 }
945
946 #[unsafe(no_mangle)]
949 pub extern "C-unwind" fn file_drop(file: *const File) {
950 assert!(!file.is_null());
951
952 drop(unsafe { Box::from_raw(file.cast_mut()) });
953 }
954
955 #[unsafe(no_mangle)]
959 pub extern "C-unwind" fn file_cloneRef(file: *const File) -> *const File {
960 let file = unsafe { file.as_ref() }.unwrap();
961 Box::into_raw(Box::new(file.clone()))
962 }
963
964 #[unsafe(no_mangle)]
966 pub extern "C-unwind" fn file_getStatus(file: *const File) -> FileState {
967 assert!(!file.is_null());
968
969 let file = unsafe { &*file };
970
971 file.borrow().state()
972 }
973
974 #[unsafe(no_mangle)]
978 pub unsafe extern "C-unwind" fn file_addListener(
979 file: *const File,
980 listener: *mut c::StatusListener,
981 ) {
982 assert!(!file.is_null());
983 assert!(!listener.is_null());
984
985 let file = unsafe { &*file };
986
987 file.borrow_mut()
988 .add_legacy_listener(HostTreePointer::new(listener));
989 }
990
991 #[unsafe(no_mangle)]
993 pub extern "C-unwind" fn file_removeListener(
994 file: *const File,
995 listener: *mut c::StatusListener,
996 ) {
997 assert!(!file.is_null());
998 assert!(!listener.is_null());
999
1000 let file = unsafe { &*file };
1001
1002 file.borrow_mut().remove_legacy_listener(listener);
1003 }
1004
1005 #[unsafe(no_mangle)]
1008 pub extern "C-unwind" fn file_getCanonicalHandle(file: *const File) -> libc::uintptr_t {
1009 assert!(!file.is_null());
1010
1011 let file = unsafe { &*file };
1012
1013 file.canonical_handle()
1014 }
1015}
1016
1017#[cfg(test)]
1018mod tests {
1019 use super::*;
1020 use crate::host::syscall::Trigger;
1021 use crate::host::syscall::condition::SyscallCondition;
1022 use crate::host::syscall::types::{
1023 Blocked, Failed, SyscallError, SyscallReturn, SyscallReturnBlocked, SyscallReturnDone,
1024 };
1025
1026 #[test]
1027 #[cfg_attr(miri, ignore)]
1029 fn test_syscallresult_roundtrip() {
1030 for val in vec![
1031 Ok(1.into()),
1032 Err(linux_api::errno::Errno::EPERM.into()),
1033 Err(SyscallError::Failed(Failed {
1034 errno: linux_api::errno::Errno::EINTR,
1035 restartable: true,
1036 })),
1037 Err(SyscallError::Failed(Failed {
1038 errno: linux_api::errno::Errno::EINTR,
1039 restartable: false,
1040 })),
1041 Err(SyscallError::Blocked(Blocked {
1042 condition: SyscallCondition::new(Trigger::from(c::Trigger {
1043 type_: 1,
1044 object: c::TriggerObject {
1045 as_pointer: std::ptr::null_mut(),
1046 },
1047 state: FileState::CLOSED,
1048 })),
1049 restartable: true,
1050 })),
1051 ]
1052 .drain(..)
1053 {
1054 let orig_debug = format!("{:?}", &val);
1058 let roundtripped = SyscallResult::from(SyscallReturn::from(val));
1059 let roundtripped_debug = format!("{roundtripped:?}");
1060 assert_eq!(orig_debug, roundtripped_debug);
1061 }
1062 }
1063
1064 #[test]
1065 #[cfg_attr(miri, ignore)]
1067 fn test_syscallreturn_roundtrip() {
1068 let condition = SyscallCondition::new(Trigger::from(c::Trigger {
1069 type_: 1,
1070 object: c::TriggerObject {
1071 as_pointer: std::ptr::null_mut(),
1072 },
1073 state: FileState::CLOSED,
1074 }));
1075 for val in vec![
1076 SyscallReturn::Done(SyscallReturnDone {
1077 retval: 1.into(),
1078 restartable: false,
1079 }),
1080 SyscallReturn::Block(SyscallReturnBlocked {
1081 cond: condition.into_inner(),
1082 restartable: true,
1083 }),
1084 SyscallReturn::Native,
1085 ]
1086 .drain(..)
1087 {
1088 let orig_debug = format!("{:?}", &val);
1093 let roundtripped = SyscallReturn::from(SyscallResult::from(val));
1094 let roundtripped_debug = format!("{roundtripped:?}");
1095 assert_eq!(orig_debug, roundtripped_debug);
1096 }
1097 }
1098
1099 #[test]
1100 fn test_file_mode_o_flags() {
1101 assert_eq!(
1103 FileMode::from_o_flags(OFlag::O_PATH),
1104 Ok((FileMode::empty(), OFlag::empty()))
1105 );
1106 assert_eq!(
1107 FileMode::from_o_flags(OFlag::O_WRONLY),
1108 Ok((FileMode::WRITE, OFlag::empty()))
1109 );
1110 assert_eq!(
1111 FileMode::from_o_flags(OFlag::O_RDWR),
1112 Ok((FileMode::READ | FileMode::WRITE, OFlag::empty()))
1113 );
1114 assert_eq!(
1115 FileMode::from_o_flags(OFlag::O_RDONLY),
1116 Ok((FileMode::READ, OFlag::empty()))
1117 );
1118 assert_eq!(
1119 FileMode::from_o_flags(OFlag::empty()),
1120 Ok((FileMode::READ, OFlag::empty()))
1121 );
1122 assert_eq!(
1123 FileMode::from_o_flags(OFlag::O_RDWR | OFlag::O_WRONLY),
1124 Err(())
1125 );
1126 assert_eq!(
1127 FileMode::from_o_flags(OFlag::O_RDWR | OFlag::O_RDONLY),
1128 Ok((FileMode::READ | FileMode::WRITE, OFlag::empty()))
1129 );
1130 assert_eq!(
1131 FileMode::from_o_flags(OFlag::O_WRONLY | OFlag::O_RDONLY),
1132 Ok((FileMode::WRITE, OFlag::empty()))
1133 );
1134 assert_eq!(
1135 FileMode::from_o_flags(OFlag::O_PATH | OFlag::O_WRONLY),
1136 Err(())
1137 );
1138 assert_eq!(
1139 FileMode::from_o_flags(OFlag::O_WRONLY | OFlag::O_CLOEXEC),
1140 Ok((FileMode::WRITE, OFlag::O_CLOEXEC))
1141 );
1142
1143 assert_eq!(FileMode::as_o_flags(&FileMode::empty()), OFlag::O_PATH);
1145 assert_eq!(FileMode::as_o_flags(&FileMode::READ), OFlag::O_RDONLY);
1146 assert_eq!(FileMode::as_o_flags(&FileMode::WRITE), OFlag::O_WRONLY);
1147 assert_eq!(
1148 FileMode::as_o_flags(&(FileMode::READ | FileMode::WRITE)),
1149 OFlag::O_RDWR
1150 );
1151 }
1152}