Skip to content

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 18, 2023
1 parent 266effe commit 5946561
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 17 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 @@ -292,6 +292,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
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 5946561

Please sign in to comment.