wasmer_wasix/syscalls/wasi/
fd_renumber.rs1use super::*;
2use crate::fs::MAX_FD;
3use crate::syscalls::*;
4use std::{future::Future, pin::Pin, sync::Arc, task::Context, task::Poll};
5use virtual_fs::VirtualFile;
6
7struct FlushPoller {
8 file: Arc<std::sync::RwLock<Box<dyn VirtualFile + Send + Sync>>>,
9}
10
11impl Future for FlushPoller {
12 type Output = Result<(), Errno>;
13
14 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
15 let mut file = self.file.write().unwrap();
16 Pin::new(file.as_mut())
17 .poll_flush(cx)
18 .map_err(|_| Errno::Io)
19 }
20}
21
22#[instrument(level = "trace", skip_all, fields(%from, %to), ret)]
30pub fn fd_renumber(
31 mut ctx: FunctionEnvMut<'_, WasiEnv>,
32 from: WasiFd,
33 to: WasiFd,
34) -> Result<Errno, WasiError> {
35 WasiEnv::do_pending_operations(&mut ctx)?;
36
37 let ret = fd_renumber_internal(&mut ctx, from, to)?;
38 let env = ctx.data();
39
40 if ret == Errno::Success {
41 #[cfg(feature = "journal")]
42 if env.enable_journal {
43 JournalEffector::save_fd_renumber(&mut ctx, from, to).map_err(|err| {
44 tracing::error!("failed to save file descriptor renumber event - {}", err);
45 WasiError::Exit(ExitCode::from(Errno::Fault))
46 })?;
47 }
48 }
49
50 Ok(ret)
51}
52
53pub(crate) fn fd_renumber_internal(
54 ctx: &mut FunctionEnvMut<'_, WasiEnv>,
55 from: WasiFd,
56 to: WasiFd,
57) -> Result<Errno, WasiError> {
58 if to > MAX_FD {
59 return Ok(Errno::Badf);
60 }
61 let env = ctx.data();
62 let (_, mut state) = unsafe { env.get_memory_and_wasi_state(&ctx, 0) };
63
64 let old_fd;
68 {
69 let mut fd_map = state.fs.fd_map.write().unwrap();
70
71 let fd_entry = wasi_try_ok!(fd_map.get(from).ok_or(Errno::Badf));
73 if from == to {
74 return Ok(Errno::Success);
75 }
76
77 if let Some(target_fd) = fd_map.get(to)
79 && !target_fd.is_stdio
80 && target_fd.inode.is_preopened
81 {
82 warn!("Refusing fd_renumber({from}, {to}) because FD {to} is pre-opened");
83 return Ok(Errno::Notsup);
84 }
85
86 let new_fd_entry = Fd {
87 inner: FdInner {
88 offset: fd_entry.inner.offset.clone(),
89 rights: fd_entry.inner.rights_inheriting,
90 fd_flags: {
91 let mut f = fd_entry.inner.fd_flags;
92 f.set(Fdflagsext::CLOEXEC, false);
93 f
94 },
95 ..fd_entry.inner
96 },
97 inode: fd_entry.inode.clone(),
98 ..*fd_entry
99 };
100
101 old_fd = fd_map.remove(to);
104
105 if !fd_map.insert(true, to, new_fd_entry) {
106 panic!("Internal error: expected FD {to} to be free after closing in fd_renumber");
107 }
108 }
109 let flush_target = old_fd.as_ref().and_then(|fd_entry| {
113 let guard = fd_entry.inode.read();
114 match guard.deref() {
115 Kind::File {
116 handle: Some(file), ..
117 } => Some(file.clone()),
118 _ => None,
119 }
120 });
121 drop(old_fd);
122
123 if let Some(file) = flush_target {
124 let _ = __asyncify_light(env, None, FlushPoller { file })?;
125 }
126
127 Ok(Errno::Success)
128}