diff --git a/host/src/filesystem.rs b/host/src/filesystem.rs index 2e7a7be0..b21c970b 100644 --- a/host/src/filesystem.rs +++ b/host/src/filesystem.rs @@ -218,7 +218,24 @@ impl wasi_filesystem::WasiFilesystem for WasiCtx { &mut self, fd: wasi_filesystem::Descriptor, ) -> HostResult<(), wasi_filesystem::Errno> { - todo!() + let table = self.table(); + if table.is::>(fd) { + Ok(Ok(table + .get_file(fd) + .map_err(convert)? + .datasync() + .await + .map_err(convert)?)) + } else if table.is::>(fd) { + Ok(Ok(table + .get_dir(fd) + .map_err(convert)? + .datasync() + .await + .map_err(convert)?)) + } else { + Err(wasi_filesystem::Errno::Badf.into()) + } } async fn flags( @@ -382,7 +399,24 @@ impl wasi_filesystem::WasiFilesystem for WasiCtx { &mut self, fd: wasi_filesystem::Descriptor, ) -> HostResult<(), wasi_filesystem::Errno> { - todo!() + let table = self.table(); + if table.is::>(fd) { + Ok(Ok(table + .get_file(fd) + .map_err(convert)? + .sync() + .await + .map_err(convert)?)) + } else if table.is::>(fd) { + Ok(Ok(table + .get_dir(fd) + .map_err(convert)? + .sync() + .await + .map_err(convert)?)) + } else { + Err(wasi_filesystem::Errno::Badf.into()) + } } async fn create_directory_at( diff --git a/host/tests/runtime.rs b/host/tests/runtime.rs index ce38ad66..53628b15 100644 --- a/host/tests/runtime.rs +++ b/host/tests/runtime.rs @@ -271,6 +271,31 @@ async fn run_file_append(mut store: Store, wasi: Wasi) -> Result<()> { Ok(()) } +async fn run_file_dir_sync(mut store: Store, wasi: Wasi) -> Result<()> { + let dir = tempfile::tempdir()?; + + std::fs::File::create(dir.path().join("bar.txt"))? + .write_all(b"'Twas brillig, and the slithy toves.\n")?; + + let descriptor = + store + .data_mut() + .push_dir(Box::new(wasi_cap_std_sync::dir::Dir::from_cap_std( + Dir::from_std_file(std::fs::File::open(dir.path())?), + )))?; + + wasi.command( + &mut store, + 0 as host::Descriptor, + 1 as host::Descriptor, + &[], + &[], + &[(descriptor, "/")], + ) + .await? + .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) +} + async fn run_exit_success(mut store: Store, wasi: Wasi) -> Result<()> { let r = wasi .command( diff --git a/src/lib.rs b/src/lib.rs index 9714469b..ee218321 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -847,14 +847,14 @@ pub unsafe extern "C" fn fd_readdir( let dir = state.get_dir(fd)?; iter = DirEntryIterator { state, - cookie: 0, + cookie: wasi::DIRCOOKIE_START, use_cache: false, stream: DirEntryStream(wasi_filesystem::readdir(dir.fd)?), }; // Skip to the entry that is requested by the `cookie` // parameter. - for _ in 0..cookie { + for _ in wasi::DIRCOOKIE_START..cookie { match iter.next() { Some(Ok(_)) => {} Some(Err(e)) => return Err(e), @@ -2205,7 +2205,7 @@ impl State { dirent_cache: DirentCache { stream: Cell::new(None), for_fd: Cell::new(0), - cookie: Cell::new(0), + cookie: Cell::new(wasi::DIRCOOKIE_START), cached_dirent: Cell::new(wasi::Dirent { d_next: 0, d_ino: 0, diff --git a/test-programs/src/bin/file_dir_sync.rs b/test-programs/src/bin/file_dir_sync.rs new file mode 100644 index 00000000..26e9c907 --- /dev/null +++ b/test-programs/src/bin/file_dir_sync.rs @@ -0,0 +1,11 @@ +fn main() -> std::io::Result<()> { + let file = std::fs::File::open("bar.txt")?; + file.sync_all()?; + file.sync_data()?; + + let dir = std::fs::File::open(".")?; + dir.sync_all()?; + dir.sync_data()?; + + Ok(()) +} diff --git a/wasi-common/cap-std-sync/src/dir.rs b/wasi-common/cap-std-sync/src/dir.rs index 93e4e114..fd5077ed 100644 --- a/wasi-common/cap-std-sync/src/dir.rs +++ b/wasi-common/cap-std-sync/src/dir.rs @@ -80,11 +80,6 @@ impl Dir { Ok(File::from_cap_std(f)) } - pub fn get_fdflags(&self) -> Result { - let fdflags = get_fd_flags(&self.0)?; - Ok(fdflags) - } - pub fn open_dir_(&self, symlink_follow: bool, path: &str) -> Result { let d = if symlink_follow { self.0.open_dir(Path::new(path))? @@ -135,6 +130,42 @@ impl WasiDir for Dir { Ok(Box::new(d)) } + async fn datasync(&self) -> Result<(), Error> { + #[cfg(unix)] + { + // We open directories with `O_PATH` which doesn't permit us to + // sync the handle we have, so we open `.` to get a new one. + Ok(self.0.open(std::path::Component::CurDir)?.sync_data()?) + } + + #[cfg(windows)] + { + // Windows doesn't have any concept of ensuring that directory + // entries are sync'd. See + // https://github.com/WebAssembly/wasi-filesystem/issues/79 + Ok(()) + } + } + + async fn sync(&self) -> Result<(), Error> { + #[cfg(unix)] + { + // As above, open `.` to get a new handle. + Ok(self.0.open(std::path::Component::CurDir)?.sync_all()?) + } + + #[cfg(windows)] + { + // As above, see above. + Ok(()) + } + } + + async fn get_fdflags(&self) -> Result { + let fdflags = get_fd_flags(&self.0)?; + Ok(fdflags) + } + async fn create_dir(&self, path: &str) -> Result<(), Error> { self.0.create_dir(Path::new(path))?; Ok(()) diff --git a/wasi-common/cap-std-sync/src/file.rs b/wasi-common/cap-std-sync/src/file.rs index 42b27a90..62efd121 100644 --- a/wasi-common/cap-std-sync/src/file.rs +++ b/wasi-common/cap-std-sync/src/file.rs @@ -40,11 +40,11 @@ impl WasiFile for File { Ok(Box::new(Self(clone))) } - async fn datasync(&mut self) -> Result<(), Error> { + async fn datasync(&self) -> Result<(), Error> { self.0.sync_data()?; Ok(()) } - async fn sync(&mut self) -> Result<(), Error> { + async fn sync(&self) -> Result<(), Error> { self.0.sync_all()?; Ok(()) } diff --git a/wasi-common/src/dir.rs b/wasi-common/src/dir.rs index 9b331c7d..29249aec 100644 --- a/wasi-common/src/dir.rs +++ b/wasi-common/src/dir.rs @@ -27,6 +27,10 @@ pub trait WasiDir: Send + Sync { Err(Error::not_supported()) } + async fn datasync(&self) -> Result<(), Error>; + + async fn sync(&self) -> Result<(), Error>; + async fn get_fdflags(&self) -> Result { Ok(FdFlags::empty()) } diff --git a/wasi-common/src/file.rs b/wasi-common/src/file.rs index 642c5f4e..29358d36 100644 --- a/wasi-common/src/file.rs +++ b/wasi-common/src/file.rs @@ -26,13 +26,9 @@ pub trait WasiFile: Send + Sync { Err(Error::badf()) } - async fn datasync(&mut self) -> Result<(), Error> { - Ok(()) - } + async fn datasync(&self) -> Result<(), Error>; - async fn sync(&mut self) -> Result<(), Error> { - Ok(()) - } + async fn sync(&self) -> Result<(), Error>; async fn get_fdflags(&self) -> Result { Ok(FdFlags::empty()) diff --git a/wasi-common/tokio/src/file.rs b/wasi-common/tokio/src/file.rs index 15852dc7..74428b9b 100644 --- a/wasi-common/tokio/src/file.rs +++ b/wasi-common/tokio/src/file.rs @@ -107,10 +107,10 @@ macro_rules! wasi_file_impl { async fn try_clone(&mut self) -> Result, Error> { block_on_dummy_executor(|| self.0.try_clone()) } - async fn datasync(&mut self) -> Result<(), Error> { + async fn datasync(&self) -> Result<(), Error> { block_on_dummy_executor(|| self.0.datasync()) } - async fn sync(&mut self) -> Result<(), Error> { + async fn sync(&self) -> Result<(), Error> { block_on_dummy_executor(|| self.0.sync()) } async fn get_filetype(&mut self) -> Result {