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    wasi_try_mem_ok!(new_offset_ref.write(new_offset));
42
43    trace!(
44        %new_offset,
45    );
46
47    Ok(Errno::Success)
48}
49
50pub(crate) fn fd_seek_internal(
51    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
52    fd: WasiFd,
53    offset: FileDelta,
54    whence: Whence,
55) -> Result<Result<Filesize, Errno>, WasiError> {
56    let env = ctx.data();
57    let state = env.state.clone();
58    let (memory, _) = unsafe { env.get_memory_and_wasi_state(&ctx, 0) };
59    let fd_entry = wasi_try_ok_ok!(state.fs.get_fd(fd));
60
61    if !fd_entry.inner.rights.contains(Rights::FD_SEEK) {
62        return Ok(Err(Errno::Access));
63    }
64
65    // TODO: handle case if fd is a dir?
66    let new_offset = match whence {
67        Whence::Cur => {
68            let fd_offset = &fd_entry.inner.offset;
69
70            #[allow(clippy::comparison_chain)]
71            if offset > 0 {
72                let offset = offset as u64;
73                fd_offset.fetch_add(offset, Ordering::AcqRel) + offset
74            } else if offset < 0 {
75                let offset = offset.unsigned_abs();
76
77                wasi_try_ok_ok!(
78                    fd_offset
79                        .fetch_sub(offset, Ordering::AcqRel)
80                        .checked_sub(offset)
81                        .ok_or(Errno::Inval)
82                )
83            } else {
84                fd_offset.load(Ordering::Acquire)
85            }
86        }
87        Whence::End => {
88            use std::io::SeekFrom;
89            let mut guard = fd_entry.inode.write();
90            let deref_mut = guard.deref_mut();
91            match deref_mut {
92                Kind::File { handle, .. } => {
93                    // TODO: remove allow once inodes are refactored (see comments on [`WasiState`])
94                    #[allow(clippy::await_holding_lock)]
95                    if let Some(handle) = handle {
96                        let handle = handle.clone();
97                        let fd_offset = fd_entry.inner.offset.clone();
98                        drop(guard);
99
100                        wasi_try_ok_ok!(__asyncify(ctx, None, async move {
101                            let mut handle = handle.write().unwrap();
102                            let end = handle
103                                .seek(SeekFrom::End(offset))
104                                .await
105                                .map_err(map_io_err)?;
106
107                            // Keep updating the original file description offset even if this
108                            // numeric FD was concurrently closed or reused.
109                            fd_offset.store(end, Ordering::Release);
110                            Ok(())
111                        })?);
112                    } else {
113                        return Ok(Err(Errno::Inval));
114                    }
115                }
116                Kind::Symlink { .. } => {
117                    unimplemented!("wasi::fd_seek not implemented for symlinks")
118                }
119                Kind::Dir { .. }
120                | Kind::Root { .. }
121                | Kind::Socket { .. }
122                | Kind::PipeRx { .. }
123                | Kind::PipeTx { .. }
124                | Kind::DuplexPipe { .. }
125                | Kind::EventNotifications { .. }
126                | Kind::Epoll { .. } => {
127                    // TODO: check this
128                    return Ok(Err(Errno::Inval));
129                }
130                Kind::Buffer { .. } => {
131                    // seeking buffers probably makes sense
132                    // FIXME: implement this
133                    return Ok(Err(Errno::Inval));
134                }
135            }
136            fd_entry.inner.offset.load(Ordering::Acquire)
137        }
138        Whence::Set => {
139            let offset: u64 = wasi_try_ok_ok!(u64::try_from(offset).map_err(|_| Errno::Inval));
140
141            fd_entry.inner.offset.store(offset, Ordering::Release);
142            offset
143        }
144        _ => return Ok(Err(Errno::Inval)),
145    };
146
147    Ok(Ok(new_offset))
148}