1use 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
32pub use linux_api::fcntl::FileStatusOFlag as FileStatus;
34#[derive(Copy, Clone, Eq, PartialEq, Debug)]
37pub enum DropPosixRecordLocks {
38 False,
43 ForPid(ProcessId),
45}
46
47bitflags::bitflags! {
48 #[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 match *self {
67 READ_AND_WRITE => OFlag::O_RDWR,
68 Self::READ => OFlag::O_RDONLY,
69 Self::WRITE => OFlag::O_WRONLY,
70 EMPTY => OFlag::O_PATH,
72 _ => panic!("Invalid file mode flags"),
73 }
74 }
75
76 #[allow(clippy::result_unit_err)]
79 pub fn from_o_flags(flags: OFlag) -> Result<(Self, OFlag), ()> {
80 let mode = flags & (OFlag::O_ACCMODE | OFlag::O_PATH);
83 let remaining = flags - (OFlag::O_ACCMODE | OFlag::O_PATH);
84
85 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 #[derive(Default, Copy, Clone, Debug)]
109 #[repr(transparent)]
110 pub struct FileState: u16 {
111 #[deprecated(note = "use `FileState::empty()`")]
113 const NONE = 0;
114 const ACTIVE = 1 << 0;
119 const READABLE = 1 << 1;
121 const WRITABLE = 1 << 2;
123 const CLOSED = 1 << 3;
125 const FUTEX_WAKEUP = 1 << 4;
127 const CHILD_EVENT = 1 << 5;
129 const SOCKET_ALLOWING_CONNECT = 1 << 6;
132 const RDHUP = 1 << 7;
135 }
136}
137
138bitflags::bitflags! {
139 #[derive(Default, Copy, Clone, Debug)]
141 #[repr(transparent)]
142 pub struct FileSignals: u32 {
143 const READ_BUFFER_GREW = 1 << 0;
145 }
146}
147
148#[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
158impl 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
234pub 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
243pub 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#[derive(Clone, Debug)]
380pub struct OpenFile {
381 inner: Arc<OpenFileInner>,
382 _counter: ObjectCounter,
383}
384
385impl 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 warn_and_debug_panic!("Creating an `OpenFile` object for a closed file");
397 }
398
399 if file.has_open_file() {
400 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 pub fn close(self, cb_queue: &mut CallbackQueue) -> Option<Result<(), SyscallError>> {
423 let OpenFile { inner, _counter } = self;
424
425 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 let _ = CallbackQueue::queue_and_run_with_legacy(|cb_queue| self.close_helper(cb_queue));
460 }
461}
462
463#[derive(Debug, Clone)]
466pub struct Descriptor {
467 file: Option<CompatFile>,
471 flags: DescriptorFlags,
473 _counter: ObjectCounter,
474}
475
476impl 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 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 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 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 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#[derive(Debug)]
617pub struct CountedLegacyFileRef(HostTreePointer<c::LegacyFile>);
618
619impl CountedLegacyFileRef {
620 pub fn new(ptr: HostTreePointer<c::LegacyFile>) -> Self {
623 Self(ptr)
624 }
625
626 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 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 unsafe { c::legacyfile_unref(self.0.ptr() as *mut core::ffi::c_void) };
646 }
647}
648
649#[derive(Clone, Debug)]
655pub struct LegacyFileCounter {
656 file: Option<CountedLegacyFileRef>,
657 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 fn close_helper(&mut self, host: &Host) {
717 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 if Arc::<()>::strong_count(&self.open_count) == 1 {
727 unsafe { c::legacyfile_close(file.ptr(), host) }
728 }
729 }
730
731 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#[derive(Clone, Debug)]
759pub enum CompatFile {
760 New(OpenFile),
761 Legacy(LegacyFileCounter),
762}
763
764impl CompatFile {
765 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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 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 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 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}