Path: blob/main/crates/wasi-common/src/tokio/dir.rs
1692 views
use crate::tokio::{block_on_dummy_executor, file::File};1use crate::{2Error, ErrorExt,3dir::{ReaddirCursor, ReaddirEntity, WasiDir},4file::{FdFlags, Filestat, OFlags},5};6use std::any::Any;7use std::path::PathBuf;89pub struct Dir(crate::sync::dir::Dir);1011impl Dir {12pub fn from_cap_std(dir: cap_std::fs::Dir) -> Self {13Dir(crate::sync::dir::Dir::from_cap_std(dir))14}15}1617#[wiggle::async_trait]18impl WasiDir for Dir {19fn as_any(&self) -> &dyn Any {20self21}22async fn open_file(23&self,24symlink_follow: bool,25path: &str,26oflags: OFlags,27read: bool,28write: bool,29fdflags: FdFlags,30) -> Result<crate::dir::OpenResult, Error> {31let f = block_on_dummy_executor(move || async move {32self.033.open_file_(symlink_follow, path, oflags, read, write, fdflags)34})?;35match f {36crate::sync::dir::OpenResult::File(f) => {37Ok(crate::dir::OpenResult::File(Box::new(File::from_inner(f))))38}39crate::sync::dir::OpenResult::Dir(d) => {40Ok(crate::dir::OpenResult::Dir(Box::new(Dir(d))))41}42}43}4445async fn create_dir(&self, path: &str) -> Result<(), Error> {46block_on_dummy_executor(|| self.0.create_dir(path))47}48async fn readdir(49&self,50cursor: ReaddirCursor,51) -> Result<Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>, Error> {52struct I(Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>);53impl Iterator for I {54type Item = Result<ReaddirEntity, Error>;55fn next(&mut self) -> Option<Self::Item> {56tokio::task::block_in_place(move || self.0.next())57}58}5960let inner = block_on_dummy_executor(move || self.0.readdir(cursor))?;61Ok(Box::new(I(inner)))62}6364async fn symlink(&self, src_path: &str, dest_path: &str) -> Result<(), Error> {65block_on_dummy_executor(move || self.0.symlink(src_path, dest_path))66}67async fn remove_dir(&self, path: &str) -> Result<(), Error> {68block_on_dummy_executor(move || self.0.remove_dir(path))69}7071async fn unlink_file(&self, path: &str) -> Result<(), Error> {72block_on_dummy_executor(move || self.0.unlink_file(path))73}74async fn read_link(&self, path: &str) -> Result<PathBuf, Error> {75block_on_dummy_executor(move || self.0.read_link(path))76}77async fn get_filestat(&self) -> Result<Filestat, Error> {78block_on_dummy_executor(|| self.0.get_filestat())79}80async fn get_path_filestat(81&self,82path: &str,83follow_symlinks: bool,84) -> Result<Filestat, Error> {85block_on_dummy_executor(move || self.0.get_path_filestat(path, follow_symlinks))86}87async fn rename(88&self,89src_path: &str,90dest_dir: &dyn WasiDir,91dest_path: &str,92) -> Result<(), Error> {93let dest_dir = dest_dir94.as_any()95.downcast_ref::<Self>()96.ok_or(Error::badf().context("failed downcast to tokio Dir"))?;97block_on_dummy_executor(98move || async move { self.0.rename_(src_path, &dest_dir.0, dest_path) },99)100}101async fn hard_link(102&self,103src_path: &str,104target_dir: &dyn WasiDir,105target_path: &str,106) -> Result<(), Error> {107let target_dir = target_dir108.as_any()109.downcast_ref::<Self>()110.ok_or(Error::badf().context("failed downcast to tokio Dir"))?;111block_on_dummy_executor(move || async move {112self.0.hard_link_(src_path, &target_dir.0, target_path)113})114}115async fn set_times(116&self,117path: &str,118atime: Option<crate::SystemTimeSpec>,119mtime: Option<crate::SystemTimeSpec>,120follow_symlinks: bool,121) -> Result<(), Error> {122block_on_dummy_executor(move || self.0.set_times(path, atime, mtime, follow_symlinks))123}124}125126#[cfg(test)]127mod test {128use super::Dir;129use crate::file::{FdFlags, OFlags};130use cap_std::ambient_authority;131132#[tokio::test(flavor = "multi_thread")]133async fn scratch_dir() {134let tempdir = tempfile::Builder::new()135.prefix("cap-std-sync")136.tempdir()137.expect("create temporary dir");138let preopen_dir = cap_std::fs::Dir::open_ambient_dir(tempdir.path(), ambient_authority())139.expect("open ambient temporary dir");140let preopen_dir = Dir::from_cap_std(preopen_dir);141crate::WasiDir::open_file(142&preopen_dir,143false,144".",145OFlags::empty(),146false,147false,148FdFlags::empty(),149)150.await151.expect("open the same directory via WasiDir abstraction");152}153154// Readdir does not work on windows, so we won't test it there.155#[cfg(not(windows))]156#[tokio::test(flavor = "multi_thread")]157async fn readdir() {158use crate::dir::{ReaddirCursor, ReaddirEntity, WasiDir};159use crate::file::{FdFlags, FileType, OFlags};160use std::collections::HashMap;161162async fn readdir_into_map(dir: &dyn WasiDir) -> HashMap<String, ReaddirEntity> {163let mut out = HashMap::new();164for readdir_result in dir165.readdir(ReaddirCursor::from(0))166.await167.expect("readdir succeeds")168{169let entity = readdir_result.expect("readdir entry is valid");170out.insert(entity.name.clone(), entity);171}172out173}174175let tempdir = tempfile::Builder::new()176.prefix("cap-std-sync")177.tempdir()178.expect("create temporary dir");179let preopen_dir = cap_std::fs::Dir::open_ambient_dir(tempdir.path(), ambient_authority())180.expect("open ambient temporary dir");181let preopen_dir = Dir::from_cap_std(preopen_dir);182183let entities = readdir_into_map(&preopen_dir).await;184assert_eq!(185entities.len(),1862,187"should just be . and .. in empty dir: {entities:?}"188);189assert!(entities.get(".").is_some());190assert!(entities.get("..").is_some());191192preopen_dir193.open_file(194false,195"file1",196OFlags::CREATE,197true,198false,199FdFlags::empty(),200)201.await202.expect("create file1");203204let entities = readdir_into_map(&preopen_dir).await;205assert_eq!(entities.len(), 3, "should be ., .., file1 {entities:?}");206assert_eq!(207entities.get(".").expect(". entry").filetype,208FileType::Directory209);210assert_eq!(211entities.get("..").expect(".. entry").filetype,212FileType::Directory213);214assert_eq!(215entities.get("file1").expect("file1 entry").filetype,216FileType::RegularFile217);218}219}220221222