wasmer_wasix/syscalls/wasi/
path_readlink.rs

1use super::*;
2use crate::syscalls::*;
3
4/// ### `path_readlink()`
5/// Read the value of a symlink
6/// Inputs:
7/// - `Fd dir_fd`
8///     The base directory from which `path` is understood
9/// - `const char *path`
10///     Pointer to UTF-8 bytes that make up the path to the symlink
11/// - `u32 path_len`
12///     The number of bytes to read from `path`
13/// - `u32 buf_len`
14///     Space available pointed to by `buf`
15/// Outputs:
16/// - `char *buf`
17///     Pointer to characters containing the path that the symlink points to
18/// - `u32 buf_used`
19///     The number of bytes written to `buf`
20#[instrument(level = "trace", skip_all, fields(%dir_fd, path = field::Empty), ret)]
21pub fn path_readlink<M: MemorySize>(
22    mut ctx: FunctionEnvMut<'_, WasiEnv>,
23    dir_fd: WasiFd,
24    path: WasmPtr<u8, M>,
25    path_len: M::Offset,
26    buf: WasmPtr<u8, M>,
27    buf_len: M::Offset,
28    buf_used: WasmPtr<M::Offset, M>,
29) -> Result<Errno, WasiError> {
30    WasiEnv::do_pending_operations(&mut ctx)?;
31
32    let env = ctx.data();
33    let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
34
35    let base_dir = wasi_try_ok!(state.fs.get_fd(dir_fd));
36    if !base_dir.inner.rights.contains(Rights::PATH_READLINK) {
37        return Ok(Errno::Access);
38    }
39    let mut path_str = unsafe { get_input_str_ok!(&memory, path, path_len) };
40    Span::current().record("path", path_str.as_str());
41
42    let inode = wasi_try_ok!(state.fs.get_inode_at_path(inodes, dir_fd, &path_str, false));
43
44    {
45        let guard = inode.read();
46        if let Kind::Symlink { relative_path, .. } = guard.deref() {
47            let rel_path_str = relative_path.to_string_lossy();
48            let buf_len: u64 = buf_len.into();
49            let bytes = rel_path_str.bytes();
50            if bytes.len() as u64 >= buf_len {
51                return Ok(Errno::Overflow);
52            }
53            let bytes: Vec<_> = bytes.collect();
54
55            let out =
56                wasi_try_mem_ok!(buf.slice(&memory, wasi_try_ok!(to_offset::<M>(bytes.len()))));
57            wasi_try_mem_ok!(out.write_slice(&bytes));
58            // should we null terminate this?
59
60            let bytes_len: M::Offset =
61                wasi_try_ok!(bytes.len().try_into().map_err(|_| Errno::Overflow));
62            wasi_try_mem_ok!(buf_used.deref(&memory).write(bytes_len));
63        } else {
64            return Ok(Errno::Inval);
65        }
66    }
67
68    Ok(Errno::Success)
69}