shadow_rs/host/descriptor/
eventfd.rs
1use std::io::{Read, Write};
2
3use linux_api::errno::Errno;
4use linux_api::ioctls::IoctlRequest;
5use shadow_shim_helper_rs::syscall_types::ForeignPtr;
6
7use crate::cshadow as c;
8use crate::host::descriptor::listener::{StateEventSource, StateListenHandle, StateListenerFilter};
9use crate::host::descriptor::{FileMode, FileSignals, FileState, FileStatus};
10use crate::host::memory_manager::MemoryManager;
11use crate::host::syscall::io::{IoVec, IoVecReader, IoVecWriter};
12use crate::host::syscall::types::{SyscallError, SyscallResult};
13use crate::utility::HostTreePointer;
14use crate::utility::callback_queue::CallbackQueue;
15
16pub struct EventFd {
17 counter: u64,
18 is_semaphore_mode: bool,
19 event_source: StateEventSource,
20 state: FileState,
21 status: FileStatus,
22 has_open_file: bool,
25}
26
27impl EventFd {
28 pub fn new(init_value: u64, is_semaphore_mode: bool, status: FileStatus) -> Self {
29 Self {
30 counter: init_value,
31 is_semaphore_mode,
32 event_source: StateEventSource::new(),
33 state: FileState::ACTIVE | FileState::WRITABLE,
34 status,
35 has_open_file: false,
36 }
37 }
38
39 pub fn status(&self) -> FileStatus {
40 self.status
41 }
42
43 pub fn set_status(&mut self, status: FileStatus) {
44 self.status = status;
45 }
46
47 pub fn mode(&self) -> FileMode {
48 FileMode::READ | FileMode::WRITE
49 }
50
51 pub fn has_open_file(&self) -> bool {
52 self.has_open_file
53 }
54
55 pub fn supports_sa_restart(&self) -> bool {
56 false
57 }
58
59 pub fn set_has_open_file(&mut self, val: bool) {
60 self.has_open_file = val;
61 }
62
63 pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
64 self.update_state(
66 FileState::CLOSED | FileState::ACTIVE | FileState::READABLE | FileState::WRITABLE,
67 FileState::CLOSED,
68 FileSignals::empty(),
69 cb_queue,
70 );
71
72 Ok(())
73 }
74
75 pub fn readv(
76 &mut self,
77 iovs: &[IoVec],
78 offset: Option<libc::off_t>,
79 _flags: libc::c_int,
80 mem: &mut MemoryManager,
81 cb_queue: &mut CallbackQueue,
82 ) -> Result<libc::ssize_t, SyscallError> {
83 if offset.is_some() {
85 return Err(Errno::ESPIPE.into());
86 }
87
88 const NUM_BYTES: usize = 8;
90
91 let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
92
93 if len < NUM_BYTES {
96 log::trace!("Reading from eventfd requires a buffer of at least {NUM_BYTES} bytes");
97 return Err(Errno::EINVAL.into());
98 }
99
100 if self.counter == 0 {
101 log::trace!("Eventfd counter is 0 and cannot be read right now");
102 return Err(Errno::EWOULDBLOCK.into());
103 }
104
105 let mut writer = IoVecWriter::new(iovs, mem);
106
107 if self.is_semaphore_mode {
109 const ONE: [u8; NUM_BYTES] = 1u64.to_ne_bytes();
110 writer.write_all(&ONE)?;
111 self.counter -= 1;
112 } else {
113 let to_write: [u8; NUM_BYTES] = self.counter.to_ne_bytes();
114 writer.write_all(&to_write)?;
115 self.counter = 0;
116 }
117
118 self.refresh_state(FileSignals::empty(), cb_queue);
119
120 Ok(NUM_BYTES.try_into().unwrap())
121 }
122
123 pub fn writev(
124 &mut self,
125 iovs: &[IoVec],
126 offset: Option<libc::off_t>,
127 _flags: libc::c_int,
128 mem: &mut MemoryManager,
129 cb_queue: &mut CallbackQueue,
130 ) -> Result<libc::ssize_t, SyscallError> {
131 if offset.is_some() {
133 return Err(Errno::ESPIPE.into());
134 }
135
136 const NUM_BYTES: usize = 8;
139
140 let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
141
142 if len < NUM_BYTES {
145 log::trace!("Writing to eventfd requires a buffer with at least {NUM_BYTES} bytes");
146 return Err(Errno::EINVAL.into());
147 }
148
149 if iovs.len() > 1 {
150 return Err(Errno::EINVAL.into());
152 }
153
154 let mut reader = IoVecReader::new(iovs, mem);
155
156 let mut read_buf = [0u8; NUM_BYTES];
157 reader.read_exact(&mut read_buf)?;
158 let value: u64 = u64::from_ne_bytes(read_buf);
159
160 if value == u64::MAX {
161 log::trace!("We do not allow writing the max counter value");
162 return Err(Errno::EINVAL.into());
163 }
164
165 const MAX_ALLOWED: u64 = u64::MAX - 1;
166 if value > MAX_ALLOWED - self.counter {
167 log::trace!("The write value does not currently fit into the counter");
168 return Err(Errno::EWOULDBLOCK.into());
169 }
170
171 self.counter += value;
172 let signals = if self.counter > 0 {
173 FileSignals::READ_BUFFER_GREW
174 } else {
175 FileSignals::empty()
176 };
177 self.refresh_state(signals, cb_queue);
178
179 Ok(NUM_BYTES.try_into().unwrap())
180 }
181
182 pub fn ioctl(
183 &mut self,
184 request: IoctlRequest,
185 _arg_ptr: ForeignPtr<()>,
186 _memory_manager: &mut MemoryManager,
187 ) -> SyscallResult {
188 log::warn!("We do not yet handle ioctl request {request:?} on eventfds");
189 Err(Errno::EINVAL.into())
190 }
191
192 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
193 warn_once_then_debug!("We do not yet handle stat calls on eventfds");
194 Err(Errno::EINVAL.into())
195 }
196
197 pub fn add_listener(
198 &mut self,
199 monitoring_state: FileState,
200 monitoring_signals: FileSignals,
201 filter: StateListenerFilter,
202 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
203 + Send
204 + Sync
205 + 'static,
206 ) -> StateListenHandle {
207 self.event_source
208 .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
209 }
210
211 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
212 self.event_source.add_legacy_listener(ptr);
213 }
214
215 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
216 self.event_source.remove_legacy_listener(ptr);
217 }
218
219 pub fn state(&self) -> FileState {
220 self.state
221 }
222
223 fn refresh_state(&mut self, signals: FileSignals, cb_queue: &mut CallbackQueue) {
224 if self.state.contains(FileState::CLOSED) {
225 return;
226 }
227
228 let mut readable_writable = FileState::empty();
229
230 readable_writable.set(FileState::READABLE, self.counter > 0);
232 readable_writable.set(FileState::WRITABLE, self.counter < u64::MAX - 1);
234
235 self.update_state(
236 FileState::READABLE | FileState::WRITABLE,
237 readable_writable,
238 signals,
239 cb_queue,
240 );
241 }
242
243 fn update_state(
244 &mut self,
245 mask: FileState,
246 state: FileState,
247 signals: FileSignals,
248 cb_queue: &mut CallbackQueue,
249 ) {
250 let old_state = self.state;
251
252 self.state.remove(mask);
254 self.state.insert(state & mask);
255
256 self.handle_state_change(old_state, signals, cb_queue);
257 }
258
259 fn handle_state_change(
260 &mut self,
261 old_state: FileState,
262 signals: FileSignals,
263 cb_queue: &mut CallbackQueue,
264 ) {
265 let states_changed = self.state ^ old_state;
266
267 if states_changed.is_empty() && signals.is_empty() {
269 return;
270 }
271
272 self.event_source
273 .notify_listeners(self.state, states_changed, signals, cb_queue);
274 }
275}