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!(
97 "Reading from eventfd requires a buffer of at least {} bytes",
98 NUM_BYTES
99 );
100 return Err(Errno::EINVAL.into());
101 }
102
103 if self.counter == 0 {
104 log::trace!("Eventfd counter is 0 and cannot be read right now");
105 return Err(Errno::EWOULDBLOCK.into());
106 }
107
108 let mut writer = IoVecWriter::new(iovs, mem);
109
110 if self.is_semaphore_mode {
112 const ONE: [u8; NUM_BYTES] = 1u64.to_ne_bytes();
113 writer.write_all(&ONE)?;
114 self.counter -= 1;
115 } else {
116 let to_write: [u8; NUM_BYTES] = self.counter.to_ne_bytes();
117 writer.write_all(&to_write)?;
118 self.counter = 0;
119 }
120
121 self.refresh_state(FileSignals::empty(), cb_queue);
122
123 Ok(NUM_BYTES.try_into().unwrap())
124 }
125
126 pub fn writev(
127 &mut self,
128 iovs: &[IoVec],
129 offset: Option<libc::off_t>,
130 _flags: libc::c_int,
131 mem: &mut MemoryManager,
132 cb_queue: &mut CallbackQueue,
133 ) -> Result<libc::ssize_t, SyscallError> {
134 if offset.is_some() {
136 return Err(Errno::ESPIPE.into());
137 }
138
139 const NUM_BYTES: usize = 8;
142
143 let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
144
145 if len < NUM_BYTES {
148 log::trace!(
149 "Writing to eventfd requires a buffer with at least {} bytes",
150 NUM_BYTES
151 );
152 return Err(Errno::EINVAL.into());
153 }
154
155 if iovs.len() > 1 {
156 return Err(Errno::EINVAL.into());
158 }
159
160 let mut reader = IoVecReader::new(iovs, mem);
161
162 let mut read_buf = [0u8; NUM_BYTES];
163 reader.read_exact(&mut read_buf)?;
164 let value: u64 = u64::from_ne_bytes(read_buf);
165
166 if value == u64::MAX {
167 log::trace!("We do not allow writing the max counter value");
168 return Err(Errno::EINVAL.into());
169 }
170
171 const MAX_ALLOWED: u64 = u64::MAX - 1;
172 if value > MAX_ALLOWED - self.counter {
173 log::trace!("The write value does not currently fit into the counter");
174 return Err(Errno::EWOULDBLOCK.into());
175 }
176
177 self.counter += value;
178 let signals = if self.counter > 0 {
179 FileSignals::READ_BUFFER_GREW
180 } else {
181 FileSignals::empty()
182 };
183 self.refresh_state(signals, cb_queue);
184
185 Ok(NUM_BYTES.try_into().unwrap())
186 }
187
188 pub fn ioctl(
189 &mut self,
190 request: IoctlRequest,
191 _arg_ptr: ForeignPtr<()>,
192 _memory_manager: &mut MemoryManager,
193 ) -> SyscallResult {
194 log::warn!("We do not yet handle ioctl request {request:?} on eventfds");
195 Err(Errno::EINVAL.into())
196 }
197
198 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
199 warn_once_then_debug!("We do not yet handle stat calls on eventfds");
200 Err(Errno::EINVAL.into())
201 }
202
203 pub fn add_listener(
204 &mut self,
205 monitoring_state: FileState,
206 monitoring_signals: FileSignals,
207 filter: StateListenerFilter,
208 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
209 + Send
210 + Sync
211 + 'static,
212 ) -> StateListenHandle {
213 self.event_source
214 .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
215 }
216
217 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
218 self.event_source.add_legacy_listener(ptr);
219 }
220
221 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
222 self.event_source.remove_legacy_listener(ptr);
223 }
224
225 pub fn state(&self) -> FileState {
226 self.state
227 }
228
229 fn refresh_state(&mut self, signals: FileSignals, cb_queue: &mut CallbackQueue) {
230 if self.state.contains(FileState::CLOSED) {
231 return;
232 }
233
234 let mut readable_writable = FileState::empty();
235
236 readable_writable.set(FileState::READABLE, self.counter > 0);
238 readable_writable.set(FileState::WRITABLE, self.counter < u64::MAX - 1);
240
241 self.update_state(
242 FileState::READABLE | FileState::WRITABLE,
243 readable_writable,
244 signals,
245 cb_queue,
246 );
247 }
248
249 fn update_state(
250 &mut self,
251 mask: FileState,
252 state: FileState,
253 signals: FileSignals,
254 cb_queue: &mut CallbackQueue,
255 ) {
256 let old_state = self.state;
257
258 self.state.remove(mask);
260 self.state.insert(state & mask);
261
262 self.handle_state_change(old_state, signals, cb_queue);
263 }
264
265 fn handle_state_change(
266 &mut self,
267 old_state: FileState,
268 signals: FileSignals,
269 cb_queue: &mut CallbackQueue,
270 ) {
271 let states_changed = self.state ^ old_state;
272
273 if states_changed.is_empty() && signals.is_empty() {
275 return;
276 }
277
278 self.event_source
279 .notify_listeners(self.state, states_changed, signals, cb_queue);
280 }
281}