wasmer_wasix/syscalls/wasi/
fd_seek.rs

1use super::*;
2use crate::syscalls::*;
3
4/// ### `fd_seek()`
5/// Update file descriptor offset
6/// Inputs:
7/// - `Fd fd`
8///     File descriptor to mutate
9/// - `FileDelta offset`
10///     Number of bytes to adjust offset by
11/// - `Whence whence`
12///     What the offset is relative to
13/// Output:
14/// - `Filesize *fd`
15///     The new offset relative to the start of the file
16#[instrument(level = "trace", skip_all, fields(%fd, %offset, ?whence), ret)]
17pub fn fd_seek<M: MemorySize>(
18    mut ctx: FunctionEnvMut<'_, WasiEnv>,
19    fd: WasiFd,
20    offset: FileDelta,
21    whence: Whence,
22    newoffset: WasmPtr<Filesize, M>,
23) -> Result<Errno, WasiError> {
24    WasiEnv::do_pending_operations(&mut ctx)?;
25
26    let new_offset = wasi_try_ok!(fd_seek_internal(&mut ctx, fd, offset, whence)?);
27    let env = ctx.data();
28
29    #[cfg(feature = "journal")]
30    if env.enable_journal {
31        JournalEffector::save_fd_seek(&mut ctx, fd, offset, whence).map_err(|err| {
32            tracing::error!("failed to save file descriptor seek event - {}", err);
33            WasiError::Exit(ExitCode::from(Errno::Fault))
34        })?;
35    }
36
37    // reborrow
38    let env = ctx.data();
39    let memory = unsafe { env.memory_view(&ctx) };
40    let new_offset_ref = newoffset.deref(&memory);
41    let fd_entry = wasi_try_ok!(env.state.fs.get_fd(fd));
42    wasi_try_mem_ok!(new_offset_ref.write(new_offset));
43
44    trace!(
45        %new_offset,
46    );
47
48    Ok(Errno::Success)
49}
50
51pub(crate) fn fd_seek_internal(
52    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
53    fd: WasiFd,
54    offset: FileDelta,
55    whence: Whence,
56) -> Result<Result<Filesize, Errno>, WasiError> {
57    let env = ctx.data();
58    let state = env.state.clone();
59    let (memory, _) = unsafe { env.get_memory_and_wasi_state(&ctx, 0) };
60    let fd_entry = wasi_try_ok_ok!(state.fs.get_fd(fd));
61
62    if !fd_entry.inner.rights.contains(Rights::FD_SEEK) {
63        return Ok(Err(Errno::Access));
64    }
65
66    // TODO: handle case if fd is a dir?
67    let new_offset = match whence {
68        Whence::Cur => {
69            let mut fd_map = state.fs.fd_map.write().unwrap();
70            let fd_entry = wasi_try_ok_ok!(fd_map.get_mut(fd).ok_or(Errno::Badf));
71
72            #[allow(clippy::comparison_chain)]
73            if offset > 0 {
74                let offset = offset as u64;
75                fd_entry.offset.fetch_add(offset, Ordering::AcqRel) + offset
76            } else if offset < 0 {
77                let offset = offset.unsigned_abs();
78
79                wasi_try_ok_ok!(
80                    fd_entry
81                        .offset
82                        .fetch_sub(offset, Ordering::AcqRel)
83                        .checked_sub(offset)
84                        .ok_or(Errno::Inval)
85                )
86            } else {
87                fd_entry.offset.load(Ordering::Acquire)
88            }
89        }
90        Whence::End => {
91            use std::io::SeekFrom;
92            let mut guard = fd_entry.inode.write();
93            let deref_mut = guard.deref_mut();
94            match deref_mut {
95                Kind::File { handle, .. } => {
96                    // TODO: remove allow once inodes are refactored (see comments on [`WasiState`])
97                    #[allow(clippy::await_holding_lock)]
98                    if let Some(handle) = handle {
99                        let handle = handle.clone();
100                        drop(guard);
101
102                        wasi_try_ok_ok!(__asyncify(ctx, None, async move {
103                            let mut handle = handle.write().unwrap();
104                            let end = handle
105                                .seek(SeekFrom::End(offset))
106                                .await
107                                .map_err(map_io_err)?;
108
109                            // TODO: handle case if fd_entry.offset uses 64 bits of a u64
110                            drop(handle);
111                            let mut fd_map = state.fs.fd_map.write().unwrap();
112                            let fd_entry = fd_map.get_mut(fd).ok_or(Errno::Badf)?;
113                            fd_entry.offset.store(end, Ordering::Release);
114                            Ok(())
115                        })?);
116                    } else {
117                        return Ok(Err(Errno::Inval));
118                    }
119                }
120                Kind::Symlink { .. } => {
121                    unimplemented!("wasi::fd_seek not implemented for symlinks")
122                }
123                Kind::Dir { .. }
124                | Kind::Root { .. }
125                | Kind::Socket { .. }
126                | Kind::PipeRx { .. }
127                | Kind::PipeTx { .. }
128                | Kind::DuplexPipe { .. }
129                | Kind::EventNotifications { .. }
130                | Kind::Epoll { .. } => {
131                    // TODO: check this
132                    return Ok(Err(Errno::Inval));
133                }
134                Kind::Buffer { .. } => {
135                    // seeking buffers probably makes sense
136                    // FIXME: implement this
137                    return Ok(Err(Errno::Inval));
138                }
139            }
140            fd_entry.inner.offset.load(Ordering::Acquire)
141        }
142        Whence::Set => {
143            let mut fd_map = state.fs.fd_map.write().unwrap();
144            let fd_entry = wasi_try_ok_ok!(fd_map.get_mut(fd).ok_or(Errno::Badf));
145            let offset: u64 = wasi_try_ok_ok!(u64::try_from(offset).map_err(|_| Errno::Inval));
146
147            fd_entry.offset.store(offset, Ordering::Release);
148            offset
149        }
150        _ => return Ok(Err(Errno::Inval)),
151    };
152
153    Ok(Ok(new_offset))
154}