use super::*;
use crate::syscalls::*;
#[instrument(level = "trace", skip_all, fields(%fd, %offset, ?whence), ret)]
pub fn fd_seek<M: MemorySize>(
mut ctx: FunctionEnvMut<'_, WasiEnv>,
fd: WasiFd,
offset: FileDelta,
whence: Whence,
newoffset: WasmPtr<Filesize, M>,
) -> Result<Errno, WasiError> {
wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?);
let new_offset = wasi_try_ok!(fd_seek_internal(&mut ctx, fd, offset, whence)?);
let env = ctx.data();
#[cfg(feature = "journal")]
if env.enable_journal {
JournalEffector::save_fd_seek(&mut ctx, fd, offset, whence).map_err(|err| {
tracing::error!("failed to save file descriptor seek event - {}", err);
WasiError::Exit(ExitCode::from(Errno::Fault))
})?;
}
let env = ctx.data();
let memory = unsafe { env.memory_view(&ctx) };
let new_offset_ref = newoffset.deref(&memory);
let fd_entry = wasi_try_ok!(env.state.fs.get_fd(fd));
wasi_try_mem_ok!(new_offset_ref.write(new_offset));
trace!(
%new_offset,
);
Ok(Errno::Success)
}
pub(crate) fn fd_seek_internal(
ctx: &mut FunctionEnvMut<'_, WasiEnv>,
fd: WasiFd,
offset: FileDelta,
whence: Whence,
) -> Result<Result<Filesize, Errno>, WasiError> {
let env = ctx.data();
let state = env.state.clone();
let (memory, _) = unsafe { env.get_memory_and_wasi_state(&ctx, 0) };
let fd_entry = wasi_try_ok_ok!(state.fs.get_fd(fd));
if !fd_entry.rights.contains(Rights::FD_SEEK) {
return Ok(Err(Errno::Access));
}
let new_offset = match whence {
Whence::Cur => {
let mut fd_map = state.fs.fd_map.write().unwrap();
let fd_entry = wasi_try_ok_ok!(fd_map.get_mut(fd).ok_or(Errno::Badf));
#[allow(clippy::comparison_chain)]
if offset > 0 {
let offset = offset as u64;
fd_entry.offset.fetch_add(offset, Ordering::AcqRel) + offset
} else if offset < 0 {
let offset = offset.unsigned_abs();
wasi_try_ok_ok!(fd_entry
.offset
.fetch_sub(offset, Ordering::AcqRel)
.checked_sub(offset)
.ok_or(Errno::Inval))
} else {
fd_entry.offset.load(Ordering::Acquire)
}
}
Whence::End => {
use std::io::SeekFrom;
let mut guard = fd_entry.inode.write();
let deref_mut = guard.deref_mut();
match deref_mut {
Kind::File { ref mut handle, .. } => {
#[allow(clippy::await_holding_lock)]
if let Some(handle) = handle {
let handle = handle.clone();
drop(guard);
wasi_try_ok_ok!(__asyncify(ctx, None, async move {
let mut handle = handle.write().unwrap();
let end = handle
.seek(SeekFrom::End(offset))
.await
.map_err(map_io_err)?;
drop(handle);
let mut fd_map = state.fs.fd_map.write().unwrap();
let fd_entry = fd_map.get_mut(fd).ok_or(Errno::Badf)?;
fd_entry.offset.store(end, Ordering::Release);
Ok(())
})?);
} else {
return Ok(Err(Errno::Inval));
}
}
Kind::Symlink { .. } => {
unimplemented!("wasi::fd_seek not implemented for symlinks")
}
Kind::Dir { .. }
| Kind::Root { .. }
| Kind::Socket { .. }
| Kind::Pipe { .. }
| Kind::EventNotifications { .. }
| Kind::Epoll { .. } => {
return Ok(Err(Errno::Inval));
}
Kind::Buffer { .. } => {
return Ok(Err(Errno::Inval));
}
}
fd_entry.offset.load(Ordering::Acquire)
}
Whence::Set => {
let mut fd_map = state.fs.fd_map.write().unwrap();
let fd_entry = wasi_try_ok_ok!(fd_map.get_mut(fd).ok_or(Errno::Badf));
let offset: u64 = wasi_try_ok_ok!(u64::try_from(offset).map_err(|_| Errno::Inval));
fd_entry.offset.store(offset, Ordering::Release);
offset
}
_ => return Ok(Err(Errno::Inval)),
};
Ok(Ok(new_offset))
}