wasmer_wasix/syscalls/wasi/
fd_close.rs

1use super::*;
2use crate::syscalls::*;
3
4/// ### `fd_close()`
5/// Close an open file descriptor
6/// For sockets this will flush the data before the socket is closed
7/// Inputs:
8/// - `Fd fd`
9///     A file descriptor mapping to an open file to close
10/// Errors:
11/// - `Errno::Isdir`
12///     If `fd` is a directory
13/// - `Errno::Badf`
14///     If `fd` is invalid or not open
15#[instrument(level = "trace", skip_all, fields(pid = ctx.data().process.pid().raw(), %fd), ret)]
16pub fn fd_close(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result<Errno, WasiError> {
17    WasiEnv::do_pending_operations(&mut ctx)?;
18
19    let env = ctx.data();
20    let (_, mut state) = unsafe { env.get_memory_and_wasi_state(&ctx, 0) };
21
22    // We don't want to allow programs that blindly close all FDs in a loop
23    // to be able to close pre-opens, as that breaks wasix-libc in rather
24    // spectacular fashion.
25    if let Ok(pfd) = state.fs.get_fd(fd) {
26        if !pfd.is_stdio && pfd.inode.is_preopened {
27            trace!("Skipping fd_close for pre-opened FD ({})", fd);
28            return Ok(Errno::Success);
29        }
30    }
31    // HACK: we use tokio files to back WASI file handles. Since tokio
32    // does writes in the background, it may miss writes if the file is
33    // closed without flushing first. Hence, we flush once here.
34    match __asyncify_light(env, None, state.fs.flush(fd))? {
35        Ok(_) | Err(Errno::Isdir) | Err(Errno::Io) | Err(Errno::Access) => {}
36        Err(e) => {
37            return Ok(e);
38        }
39    }
40    wasi_try_ok!(state.fs.close_fd(fd));
41
42    #[cfg(feature = "journal")]
43    if env.enable_journal {
44        JournalEffector::save_fd_close(&mut ctx, fd).map_err(|err| {
45            tracing::error!("failed to save close descriptor event - {}", err);
46            WasiError::Exit(ExitCode::from(Errno::Fault))
47        })?;
48    }
49
50    Ok(Errno::Success)
51}