Skip to content

Commit

Permalink
Discard 0-sized writes to files (#7638)
Browse files Browse the repository at this point in the history
* Discard 0-sized writes to files

This commit comes from #7633 where Windows and Unix would behave
differently when writing at a particular file offset. Notably Unix
semantics [indicate]:

> Before any action described below is taken, and if nbyte is zero
> and the file is a regular file, the write() function may detect
> and return errors as described below. In the absence of errors,
> or if error detection is not performed, the write() function
> shall return zero and have no other results. If nbyte is zero and
> the file is not a regular file, the results are unspecified.

These semantics are a bit easier to emulate on Windows so the host
implementation now discards any attempt to perform I/O if a zero-sized
write is detected.

[indicate]: https://man7.org/linux/man-pages/man3/write.3p.html

Closes #7633

* Discard empty writes in wasi-common as well
  • Loading branch information
alexcrichton committed Feb 14, 2024
1 parent 85ffc39 commit df67fe8
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 1 deletion.
54 changes: 53 additions & 1 deletion crates/test-programs/src/bin/preview1_file_pread_pwrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,55 @@ unsafe fn test_file_pread_pwrite(dir_fd: wasi::Fd) {
wasi::path_unlink_file(dir_fd, "file").expect("removing a file");
}

unsafe fn test_file_pwrite_and_file_pos(dir_fd: wasi::Fd) {
let path = "file2";
let file_fd = wasi::path_open(
dir_fd,
0,
path,
wasi::OFLAGS_CREAT,
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE,
0,
0,
)
.expect("opening a file");
assert!(
file_fd > libc::STDERR_FILENO as wasi::Fd,
"file descriptor range check",
);

// Perform a 0-sized pwrite at an offset beyond the end of the file. Unix
// semantics should pop out where nothing is actually written and the size
// of the file isn't modified.
assert_eq!(wasi::fd_tell(file_fd).unwrap(), 0);
let ciovec = wasi::Ciovec {
buf: [].as_ptr(),
buf_len: 0,
};
let n = wasi::fd_pwrite(file_fd, &mut [ciovec], 50).expect("writing bytes at offset 2");
assert_eq!(n, 0);

assert_eq!(wasi::fd_tell(file_fd).unwrap(), 0);
let stat = wasi::fd_filestat_get(file_fd).unwrap();
assert_eq!(stat.size, 0);

// Now write a single byte and make sure it actually works
let buf = [0];
let ciovec = wasi::Ciovec {
buf: buf.as_ptr(),
buf_len: buf.len(),
};
let n = wasi::fd_pwrite(file_fd, &mut [ciovec], 50).expect("writing bytes at offset 2");
assert_eq!(n, 1);

assert_eq!(wasi::fd_tell(file_fd).unwrap(), 0);
let stat = wasi::fd_filestat_get(file_fd).unwrap();
assert_eq!(stat.size, 51);

wasi::fd_close(file_fd).expect("closing a file");
wasi::path_unlink_file(dir_fd, path).expect("removing a file");
}

fn main() {
let mut args = env::args();
let prog = args.next().unwrap();
Expand All @@ -148,5 +197,8 @@ fn main() {
};

// Run the tests.
unsafe { test_file_pread_pwrite(dir_fd) }
unsafe {
test_file_pread_pwrite(dir_fd);
test_file_pwrite_and_file_pos(dir_fd);
}
}
3 changes: 3 additions & 0 deletions crates/wasi-common/src/sync/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ impl WasiFile for File {
bufs: &[io::IoSlice<'a>],
offset: u64,
) -> Result<u64, Error> {
if bufs.iter().map(|i| i.len()).sum::<usize>() == 0 {
return Ok(0);
}
let n = self.0.write_vectored_at(bufs, offset)?;
Ok(n.try_into()?)
}
Expand Down
3 changes: 3 additions & 0 deletions crates/wasi-common/src/tokio/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ macro_rules! wasi_file_impl {
bufs: &[io::IoSlice<'a>],
offset: u64,
) -> Result<u64, Error> {
if bufs.iter().map(|i| i.len()).sum::<usize>() == 0 {
return Ok(0);
}
block_on_dummy_executor(move || self.0.write_vectored_at(bufs, offset))
}
async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
Expand Down
4 changes: 4 additions & 0 deletions crates/wasi/src/preview2/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ impl HostOutputStream for FileOutputStream {
}
}

if buf.is_empty() {
return Ok(());
}

let f = Arc::clone(&self.file);
let m = self.mode;
let task = spawn_blocking(move || match m {
Expand Down

0 comments on commit df67fe8

Please sign in to comment.