Path: blob/main/crates/wasi-common/src/sync/file.rs
1693 views
use crate::{1Error, ErrorExt,2file::{Advice, FdFlags, FileType, Filestat, WasiFile},3};4use cap_fs_ext::MetadataExt;5use fs_set_times::{SetTimes, SystemTimeSpec};6use io_lifetimes::AsFilelike;7use std::any::Any;8use std::io::{self, IsTerminal};9use system_interface::{10fs::{FileIoExt, GetSetFdFlags},11io::{IoExt, ReadReady},12};1314pub struct File(cap_std::fs::File);1516impl File {17pub fn from_cap_std(file: cap_std::fs::File) -> Self {18File(file)19}20}2122#[wiggle::async_trait]23impl WasiFile for File {24fn as_any(&self) -> &dyn Any {25self26}27#[cfg(unix)]28fn pollable(&self) -> Option<rustix::fd::BorrowedFd<'_>> {29Some(self.0.as_fd())30}31#[cfg(windows)]32fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> {33Some(self.0.as_raw_handle_or_socket())34}35async fn datasync(&self) -> Result<(), Error> {36self.0.sync_data()?;37Ok(())38}39async fn sync(&self) -> Result<(), Error> {40self.0.sync_all()?;41Ok(())42}43async fn get_filetype(&self) -> Result<FileType, Error> {44let meta = self.0.metadata()?;45Ok(filetype_from(&meta.file_type()))46}47async fn get_fdflags(&self) -> Result<FdFlags, Error> {48let fdflags = get_fd_flags(&self.0)?;49Ok(fdflags)50}51async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {52if fdflags.intersects(53crate::file::FdFlags::DSYNC | crate::file::FdFlags::SYNC | crate::file::FdFlags::RSYNC,54) {55return Err(Error::invalid_argument().context("cannot set DSYNC, SYNC, or RSYNC flag"));56}57let set_fd_flags = self.0.new_set_fd_flags(to_sysif_fdflags(fdflags))?;58self.0.set_fd_flags(set_fd_flags)?;59Ok(())60}61async fn get_filestat(&self) -> Result<Filestat, Error> {62let meta = self.0.metadata()?;63Ok(Filestat {64device_id: meta.dev(),65inode: meta.ino(),66filetype: filetype_from(&meta.file_type()),67nlink: meta.nlink(),68size: meta.len(),69atim: meta.accessed().map(|t| Some(t.into_std())).unwrap_or(None),70mtim: meta.modified().map(|t| Some(t.into_std())).unwrap_or(None),71ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),72})73}74async fn set_filestat_size(&self, size: u64) -> Result<(), Error> {75self.0.set_len(size)?;76Ok(())77}78async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {79self.0.advise(offset, len, convert_advice(advice))?;80Ok(())81}82async fn set_times(83&self,84atime: Option<crate::SystemTimeSpec>,85mtime: Option<crate::SystemTimeSpec>,86) -> Result<(), Error> {87self.088.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;89Ok(())90}91async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {92let n = self.0.read_vectored(bufs)?;93Ok(n.try_into()?)94}95async fn read_vectored_at<'a>(96&self,97bufs: &mut [io::IoSliceMut<'a>],98offset: u64,99) -> Result<u64, Error> {100let n = self.0.read_vectored_at(bufs, offset)?;101Ok(n.try_into()?)102}103async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {104let n = self.0.write_vectored(bufs)?;105Ok(n.try_into()?)106}107async fn write_vectored_at<'a>(108&self,109bufs: &[io::IoSlice<'a>],110offset: u64,111) -> Result<u64, Error> {112if bufs.iter().map(|i| i.len()).sum::<usize>() == 0 {113return Ok(0);114}115let n = self.0.write_vectored_at(bufs, offset)?;116Ok(n.try_into()?)117}118async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {119Ok(self.0.seek(pos)?)120}121async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {122let n = self.0.peek(buf)?;123Ok(n.try_into()?)124}125fn num_ready_bytes(&self) -> Result<u64, Error> {126Ok(self.0.num_ready_bytes()?)127}128fn isatty(&self) -> bool {129#[cfg(unix)]130return self.0.as_fd().is_terminal();131#[cfg(windows)]132return self.0.as_handle().is_terminal();133}134}135136pub fn filetype_from(ft: &cap_std::fs::FileType) -> FileType {137use cap_fs_ext::FileTypeExt;138if ft.is_dir() {139FileType::Directory140} else if ft.is_symlink() {141FileType::SymbolicLink142} else if ft.is_socket() {143if ft.is_block_device() {144FileType::SocketDgram145} else {146FileType::SocketStream147}148} else if ft.is_block_device() {149FileType::BlockDevice150} else if ft.is_char_device() {151FileType::CharacterDevice152} else if ft.is_file() {153FileType::RegularFile154} else {155FileType::Unknown156}157}158159#[cfg(windows)]160use io_lifetimes::{AsHandle, BorrowedHandle};161#[cfg(windows)]162impl AsHandle for File {163fn as_handle(&self) -> BorrowedHandle<'_> {164self.0.as_handle()165}166}167168#[cfg(windows)]169use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket};170#[cfg(windows)]171impl AsRawHandleOrSocket for File {172#[inline]173fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {174self.0.as_raw_handle_or_socket()175}176}177178#[cfg(unix)]179use io_lifetimes::{AsFd, BorrowedFd};180181#[cfg(unix)]182impl AsFd for File {183fn as_fd(&self) -> BorrowedFd<'_> {184self.0.as_fd()185}186}187188pub(crate) fn convert_systimespec(t: Option<crate::SystemTimeSpec>) -> Option<SystemTimeSpec> {189match t {190Some(crate::SystemTimeSpec::Absolute(t)) => Some(SystemTimeSpec::Absolute(t.into_std())),191Some(crate::SystemTimeSpec::SymbolicNow) => Some(SystemTimeSpec::SymbolicNow),192None => None,193}194}195196pub(crate) fn to_sysif_fdflags(f: crate::file::FdFlags) -> system_interface::fs::FdFlags {197let mut out = system_interface::fs::FdFlags::empty();198if f.contains(crate::file::FdFlags::APPEND) {199out |= system_interface::fs::FdFlags::APPEND;200}201if f.contains(crate::file::FdFlags::DSYNC) {202out |= system_interface::fs::FdFlags::DSYNC;203}204if f.contains(crate::file::FdFlags::NONBLOCK) {205out |= system_interface::fs::FdFlags::NONBLOCK;206}207if f.contains(crate::file::FdFlags::RSYNC) {208out |= system_interface::fs::FdFlags::RSYNC;209}210if f.contains(crate::file::FdFlags::SYNC) {211out |= system_interface::fs::FdFlags::SYNC;212}213out214}215216/// Return the file-descriptor flags for a given file-like object.217///218/// This returns the flags needed to implement [`WasiFile::get_fdflags`].219pub fn get_fd_flags<Filelike: AsFilelike>(f: Filelike) -> io::Result<crate::file::FdFlags> {220let f = f.as_filelike().get_fd_flags()?;221let mut out = crate::file::FdFlags::empty();222if f.contains(system_interface::fs::FdFlags::APPEND) {223out |= crate::file::FdFlags::APPEND;224}225if f.contains(system_interface::fs::FdFlags::DSYNC) {226out |= crate::file::FdFlags::DSYNC;227}228if f.contains(system_interface::fs::FdFlags::NONBLOCK) {229out |= crate::file::FdFlags::NONBLOCK;230}231if f.contains(system_interface::fs::FdFlags::RSYNC) {232out |= crate::file::FdFlags::RSYNC;233}234if f.contains(system_interface::fs::FdFlags::SYNC) {235out |= crate::file::FdFlags::SYNC;236}237Ok(out)238}239240fn convert_advice(advice: Advice) -> system_interface::fs::Advice {241match advice {242Advice::Normal => system_interface::fs::Advice::Normal,243Advice::Sequential => system_interface::fs::Advice::Sequential,244Advice::Random => system_interface::fs::Advice::Random,245Advice::WillNeed => system_interface::fs::Advice::WillNeed,246Advice::DontNeed => system_interface::fs::Advice::DontNeed,247Advice::NoReuse => system_interface::fs::Advice::NoReuse,248}249}250251252