wasmer_wasix/syscalls/wasi/
fd_renumber.rs

1use super::*;
2use crate::syscalls::*;
3
4/// ### `fd_renumber()`
5/// Atomically copy file descriptor
6/// Inputs:
7/// - `Fd from`
8///     File descriptor to copy
9/// - `Fd to`
10///     Location to copy file descriptor to
11#[instrument(level = "trace", skip_all, fields(%from, %to), ret)]
12pub fn fd_renumber(
13    mut ctx: FunctionEnvMut<'_, WasiEnv>,
14    from: WasiFd,
15    to: WasiFd,
16) -> Result<Errno, WasiError> {
17    WasiEnv::do_pending_operations(&mut ctx)?;
18
19    let ret = fd_renumber_internal(&mut ctx, from, to)?;
20    let env = ctx.data();
21
22    if ret == Errno::Success {
23        #[cfg(feature = "journal")]
24        if env.enable_journal {
25            JournalEffector::save_fd_renumber(&mut ctx, from, to).map_err(|err| {
26                tracing::error!("failed to save file descriptor renumber event - {}", err);
27                WasiError::Exit(ExitCode::from(Errno::Fault))
28            })?;
29        }
30    }
31
32    Ok(ret)
33}
34
35pub(crate) fn fd_renumber_internal(
36    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
37    from: WasiFd,
38    to: WasiFd,
39) -> Result<Errno, WasiError> {
40    if from == to {
41        return Ok(Errno::Success);
42    }
43    let env = ctx.data();
44    let (_, mut state) = unsafe { env.get_memory_and_wasi_state(&ctx, 0) };
45
46    if let Ok(fd) = state.fs.get_fd(to) {
47        if !fd.is_stdio && fd.inode.is_preopened {
48            // There isn't a good hack we can do here; the code that made this call
49            // expects its new FD to be the number it asked for. This will, however,
50            // break wasix-libc when it attempts to use the FD to make any fs-related
51            // syscalls. The best we can do is warn people so they can change the code.
52            warn!(
53                "FD ({to}) is a pre-open and should not be closed, \
54                but will be closed in response to an fd_renumber operation. \
55                This will likely break stuff."
56            );
57        }
58        match __asyncify_light(env, None, state.fs.flush(to))? {
59            Ok(_) | Err(Errno::Isdir) | Err(Errno::Io) | Err(Errno::Access) => {}
60            Err(e) => {
61                return Ok(e);
62            }
63        }
64        wasi_try_ok!(state.fs.close_fd(to));
65    }
66
67    let mut fd_map = state.fs.fd_map.write().unwrap();
68    let fd_entry = wasi_try_ok!(fd_map.get(from).ok_or(Errno::Badf));
69
70    let new_fd_entry = Fd {
71        // TODO: verify this is correct
72        inner: FdInner {
73            offset: fd_entry.inner.offset.clone(),
74            rights: fd_entry.inner.rights_inheriting,
75            fd_flags: {
76                let mut f = fd_entry.inner.fd_flags;
77                f.set(Fdflagsext::CLOEXEC, false);
78                f
79            },
80            ..fd_entry.inner
81        },
82        inode: fd_entry.inode.clone(),
83        ..*fd_entry
84    };
85
86    // Exclusive insert because we expect `to` to be empty after closing it above
87    if !fd_map.insert(true, to, new_fd_entry) {
88        panic!("Internal error: expected FD {to} to be free after closing in fd_renumber");
89    }
90
91    Ok(Errno::Success)
92}