wasmer_wasix/syscalls/wasix/
epoll_ctl.rs

1use wasmer_wasix_types::wasi::{EpollCtl, EpollEvent, EpollEventCtl, SubscriptionClock, Userdata};
2
3use super::*;
4use crate::{
5    WasiInodes,
6    fs::{InodeValFilePollGuard, InodeValFilePollGuardJoin},
7    os::epoll::register_epoll_handler,
8    state::PollEventSet,
9    syscalls::*,
10};
11
12/// ### `epoll_ctl()`
13/// Modifies an epoll interest list
14/// Output:
15/// - `Fd fd`
16///   The new file handle that is used to modify or wait on the interest list
17#[instrument(level = "trace", skip_all, fields(timeout_ms = field::Empty, fd_guards = field::Empty, seen = field::Empty, fd), ret)]
18pub fn epoll_ctl<M: MemorySize + 'static>(
19    mut ctx: FunctionEnvMut<'_, WasiEnv>,
20    epfd: WasiFd,
21    op: EpollCtl,
22    fd: WasiFd,
23    event_ref: WasmPtr<EpollEvent<M>, M>,
24) -> Result<Errno, WasiError> {
25    WasiEnv::do_pending_operations(&mut ctx)?;
26
27    let env = ctx.data();
28
29    let memory = unsafe { env.memory_view(&ctx) };
30    let event = if event_ref.offset() != M::ZERO {
31        Some(wasi_try_mem_ok!(event_ref.read(&memory)))
32    } else {
33        None
34    };
35
36    let event_ctl = event.map(|evt| EpollEventCtl {
37        events: evt.events,
38        ptr: evt.data.ptr.into(),
39        fd: evt.data.fd,
40        data1: evt.data.data1,
41        data2: evt.data.data2,
42    });
43
44    wasi_try_ok!(epoll_ctl_internal(
45        &mut ctx,
46        epfd,
47        op,
48        fd,
49        event_ctl.as_ref()
50    )?);
51    let env = ctx.data();
52
53    #[cfg(feature = "journal")]
54    if env.enable_journal {
55        JournalEffector::save_epoll_ctl(&mut ctx, epfd, op, fd, event_ctl).map_err(|err| {
56            tracing::error!("failed to save epoll_create event - {}", err);
57            WasiError::Exit(ExitCode::from(Errno::Fault))
58        })?;
59    }
60
61    Ok(Errno::Success)
62}
63
64pub(crate) fn epoll_ctl_internal(
65    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
66    epfd: WasiFd,
67    op: EpollCtl,
68    fd: WasiFd,
69    event_ctl: Option<&EpollEventCtl>,
70) -> Result<Result<(), Errno>, WasiError> {
71    let env = ctx.data();
72    let fd_entry = wasi_try_ok_ok!(env.state.fs.get_fd(epfd));
73
74    let mut inode_guard = fd_entry.inode.read();
75    match inode_guard.deref() {
76        Kind::Epoll { state } => {
77            let res = match op {
78                EpollCtl::Add => {
79                    let Some(event) = event_ctl else {
80                        return Ok(Err(Errno::Inval));
81                    };
82                    let (epoll_fd, sub_state) = match state.prepare_add(fd, event) {
83                        Ok(v) => v,
84                        Err(err) => return Ok(Err(err)),
85                    };
86
87                    match register_epoll_handler(
88                        &env.state,
89                        &epoll_fd,
90                        state.clone(),
91                        sub_state.clone(),
92                    ) {
93                        Ok(fd_guard) => {
94                            if let Some(fd_guard) = fd_guard {
95                                sub_state.add_join(fd_guard);
96                            }
97                            Ok(())
98                        }
99                        Err(err) => {
100                            state.rollback_registration(fd, None);
101                            Err(err)
102                        }
103                    }
104                }
105                EpollCtl::Mod => {
106                    let Some(event) = event_ctl else {
107                        return Ok(Err(Errno::Inval));
108                    };
109                    let (epoll_fd, sub_state, old_subscription) = match state.prepare_mod(fd, event)
110                    {
111                        Ok(v) => v,
112                        Err(err) => return Ok(Err(err)),
113                    };
114                    // Detach the previous generation before installing the new
115                    // handler so dropping old guards cannot remove the new one.
116                    old_subscription.detach_joins();
117
118                    match register_epoll_handler(
119                        &env.state,
120                        &epoll_fd,
121                        state.clone(),
122                        sub_state.clone(),
123                    ) {
124                        Ok(fd_guard) => {
125                            if let Some(fd_guard) = fd_guard {
126                                sub_state.add_join(fd_guard);
127                            }
128                            Ok(())
129                        }
130                        Err(err) => {
131                            state.rollback_registration(fd, Some(old_subscription.clone()));
132                            let old_epoll_fd = old_subscription.fd_meta();
133                            match register_epoll_handler(
134                                &env.state,
135                                &old_epoll_fd,
136                                state.clone(),
137                                old_subscription.clone(),
138                            ) {
139                                Ok(fd_guard) => {
140                                    if let Some(fd_guard) = fd_guard {
141                                        old_subscription.add_join(fd_guard);
142                                    }
143                                }
144                                Err(reinstall_err) => {
145                                    // Do not leave a restored subscription without handlers.
146                                    state.rollback_registration(fd, None);
147                                    tracing::warn!(
148                                        fd,
149                                        ?err,
150                                        ?reinstall_err,
151                                        "failed to reinstall previous epoll handler after MOD failure"
152                                    );
153                                    return Ok(Err(reinstall_err));
154                                }
155                            }
156                            Err(err)
157                        }
158                    }
159                }
160                EpollCtl::Del => state.apply_del(fd),
161                EpollCtl::Unknown => Err(Errno::Inval),
162            };
163            Ok(res)
164        }
165        _ => Ok(Err(Errno::Inval)),
166    }
167}