Skip to content
This repository has been archived by the owner on Oct 13, 2023. It is now read-only.

Commit

Permalink
Implement sync and datasync on files and directories.
Browse files Browse the repository at this point in the history
On Windows, there doesn't appear to be a way to sync a directory, to
ensure that the directory entry for a file is sync'd. So for now, just
silently succeed. I've opened WebAssembly/wasi-filesystem#79 to track
this at the spec level.
  • Loading branch information
sunfishcode committed Jan 11, 2023
1 parent d56b897 commit 13a51a4
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 20 deletions.
38 changes: 36 additions & 2 deletions host/src/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Box<dyn WasiFile>>(fd) {
Ok(Ok(table
.get_file(fd)
.map_err(convert)?
.datasync()
.await
.map_err(convert)?))
} else if table.is::<Box<dyn WasiDir>>(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(
Expand Down Expand Up @@ -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::<Box<dyn WasiFile>>(fd) {
Ok(Ok(table
.get_file(fd)
.map_err(convert)?
.sync()
.await
.map_err(convert)?))
} else if table.is::<Box<dyn WasiDir>>(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(
Expand Down
25 changes: 25 additions & 0 deletions host/tests/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,31 @@ async fn run_file_append(mut store: Store<WasiCtx>, wasi: Wasi) -> Result<()> {
Ok(())
}

async fn run_file_dir_sync(mut store: Store<WasiCtx>, 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<WasiCtx>, wasi: Wasi) -> Result<()> {
let r = wasi
.command(
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 11 additions & 0 deletions test-programs/src/bin/file_dir_sync.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
41 changes: 36 additions & 5 deletions wasi-common/cap-std-sync/src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,6 @@ impl Dir {
Ok(File::from_cap_std(f))
}

pub fn get_fdflags(&self) -> Result<FdFlags, Error> {
let fdflags = get_fd_flags(&self.0)?;
Ok(fdflags)
}

pub fn open_dir_(&self, symlink_follow: bool, path: &str) -> Result<Self, Error> {
let d = if symlink_follow {
self.0.open_dir(Path::new(path))?
Expand Down Expand Up @@ -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<FdFlags, Error> {
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(())
Expand Down
4 changes: 2 additions & 2 deletions wasi-common/cap-std-sync/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
4 changes: 4 additions & 0 deletions wasi-common/src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FdFlags, Error> {
Ok(FdFlags::empty())
}
Expand Down
8 changes: 2 additions & 6 deletions wasi-common/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FdFlags, Error> {
Ok(FdFlags::empty())
Expand Down
4 changes: 2 additions & 2 deletions wasi-common/tokio/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ macro_rules! wasi_file_impl {
async fn try_clone(&mut self) -> Result<Box<dyn WasiFile>, 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<FileType, Error> {
Expand Down

0 comments on commit 13a51a4

Please sign in to comment.