wasmer_wasix/syscalls/wasix/
fd_pipe.rs

1use std::sync::atomic::AtomicUsize;
2
3use virtual_fs::Pipe;
4
5use super::*;
6use crate::syscalls::*;
7
8// Used to make pipe end names unique. This is necessary since we use
9// a hash of the name to calculate inode numbers. The actual number
10// has no other meaning.
11static PIPE_NUMBER: AtomicUsize = AtomicUsize::new(0);
12
13/// ### `fd_pipe()`
14/// Creates ta pipe that feeds data between two file handles
15/// Output:
16/// - `Fd`
17///     First file handle that represents the read end of the pipe
18/// - `Fd`
19///     Second file handle that represents the write end of the pipe
20#[instrument(level = "trace", skip_all, fields(read_fd = field::Empty, write_fd = field::Empty), ret)]
21pub fn fd_pipe<M: MemorySize>(
22    mut ctx: FunctionEnvMut<'_, WasiEnv>,
23    ro_read_fd: WasmPtr<WasiFd, M>,
24    ro_write_fd: WasmPtr<WasiFd, M>,
25) -> Result<Errno, WasiError> {
26    WasiEnv::do_pending_operations(&mut ctx)?;
27
28    let (read_fd, write_fd) = wasi_try_ok!(fd_pipe_internal(&mut ctx, None, None));
29    let env = ctx.data();
30
31    #[cfg(feature = "journal")]
32    if env.enable_journal {
33        JournalEffector::save_fd_pipe(&mut ctx, read_fd, write_fd).map_err(|err| {
34            tracing::error!("failed to save create pipe event - {}", err);
35            WasiError::Exit(ExitCode::from(Errno::Fault))
36        })?;
37    }
38
39    let env = ctx.data();
40    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
41
42    Span::current()
43        .record("read_fd", read_fd)
44        .record("write_fd", write_fd);
45
46    wasi_try_mem_ok!(ro_read_fd.write(&memory, read_fd));
47    wasi_try_mem_ok!(ro_write_fd.write(&memory, write_fd));
48
49    Ok(Errno::Success)
50}
51
52pub fn fd_pipe_internal(
53    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
54    with_read_fd: Option<WasiFd>,
55    with_write_fd: Option<WasiFd>,
56) -> Result<(WasiFd, WasiFd), Errno> {
57    let env = ctx.data();
58    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
59    let (tx, rx) = Pipe::new().split();
60
61    // FIXME: since a hash of the inode name is used to calculate the inode number, this may
62    // or may not break journals that include pipes and are compacted.
63    let pipe_no = PIPE_NUMBER.fetch_add(1, Ordering::SeqCst);
64
65    let rx_inode = state.fs.create_inode_with_default_stat(
66        inodes,
67        Kind::PipeRx { rx },
68        false,
69        format!("pipe{pipe_no}-rx").into(),
70    );
71    let tx_inode = state.fs.create_inode_with_default_stat(
72        inodes,
73        Kind::PipeTx { tx },
74        false,
75        format!("pipe{pipe_no}-tx").into(),
76    );
77
78    let rights = Rights::FD_SYNC
79        | Rights::FD_DATASYNC
80        | Rights::POLL_FD_READWRITE
81        | Rights::SOCK_SEND
82        | Rights::FD_FDSTAT_SET_FLAGS
83        | Rights::FD_FILESTAT_GET;
84
85    let read_rights = rights | Rights::FD_READ;
86    let write_rights = rights | Rights::FD_WRITE;
87
88    let read_fd = if let Some(fd) = with_read_fd {
89        state
90            .fs
91            .with_fd(
92                read_rights,
93                read_rights,
94                Fdflags::empty(),
95                Fdflagsext::empty(),
96                0,
97                rx_inode,
98                fd,
99            )
100            .map(|()| fd)?
101    } else {
102        state.fs.create_fd(
103            read_rights,
104            read_rights,
105            Fdflags::empty(),
106            Fdflagsext::empty(),
107            0,
108            rx_inode,
109        )?
110    };
111
112    let write_fd = if let Some(fd) = with_write_fd {
113        state
114            .fs
115            .with_fd(
116                write_rights,
117                write_rights,
118                Fdflags::empty(),
119                Fdflagsext::empty(),
120                0,
121                tx_inode,
122                fd,
123            )
124            .map(|()| fd)?
125    } else {
126        state.fs.create_fd(
127            write_rights,
128            write_rights,
129            Fdflags::empty(),
130            Fdflagsext::empty(),
131            0,
132            tx_inode,
133        )?
134    };
135
136    Ok((read_fd, write_fd))
137}