Skip to content

Commit e70f732

Browse files
authored
virtfs file: update cursor position on fd_read (#2070)
* virtfs file: update cursor position on fd_read If a handle is backed by InMemoryFile, fd_read (turned into Handle::read_vectored) doesn't update the cursor position properly and thus prevents the caller from detecting EOF. * virtfs file: fd_{pread,pwrite}: update offset in iovec iteration If multiple iovec's are supplied, fd_pread and fd_pwrite previously access data at the same offset for each iovec.
1 parent f8f79ba commit e70f732

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use more_asserts::assert_gt;
22
use std::{env, process};
3+
use std::convert::TryInto;
34
use wasi_tests::open_scratch_directory;
45

56
unsafe fn test_file_pread_pwrite(dir_fd: wasi::Fd) {
@@ -38,6 +39,71 @@ unsafe fn test_file_pread_pwrite(dir_fd: wasi::Fd) {
3839
assert_eq!(nread, 4, "nread bytes check");
3940
assert_eq!(contents, &[0u8, 1, 2, 3], "written bytes equal read bytes");
4041

42+
// Write all the data through multiple iovecs.
43+
//
44+
// Note that this needs to be done with a loop, because some
45+
// platforms do not support writing multiple iovecs at once.
46+
// See https://github.com/rust-lang/rust/issues/74825.
47+
let contents = &[0u8, 1, 2, 3];
48+
let mut offset = 0usize;
49+
loop {
50+
let mut ciovecs: Vec<wasi::Ciovec> = Vec::new();
51+
let mut remaining = contents.len() - offset;
52+
if remaining > 2 {
53+
ciovecs.push(
54+
wasi::Ciovec {
55+
buf: contents[offset..].as_ptr() as *const _,
56+
buf_len: 2,
57+
},
58+
);
59+
remaining -= 2;
60+
}
61+
ciovecs.push(
62+
wasi::Ciovec {
63+
buf: contents[contents.len() - remaining..].as_ptr() as *const _,
64+
buf_len: remaining
65+
},
66+
);
67+
68+
nwritten =
69+
wasi::fd_pwrite(file_fd, ciovecs.as_slice(), offset.try_into().unwrap()).expect("writing bytes at offset 0");
70+
71+
offset += nwritten;
72+
if offset == contents.len() {
73+
break;
74+
}
75+
}
76+
assert_eq!(offset, 4, "nread bytes check");
77+
78+
// Read all the data through multiple iovecs.
79+
//
80+
// Note that this needs to be done with a loop, because some
81+
// platforms do not support reading multiple iovecs at once.
82+
// See https://github.com/rust-lang/rust/issues/74825.
83+
let contents = &mut [0u8; 4];
84+
let mut offset = 0usize;
85+
loop {
86+
let buffer = &mut [0u8; 4];
87+
let iovecs = &[
88+
wasi::Iovec {
89+
buf: buffer.as_mut_ptr() as *mut _,
90+
buf_len: 2,
91+
},
92+
wasi::Iovec {
93+
buf: buffer[2..].as_mut_ptr() as *mut _,
94+
buf_len: 2
95+
},
96+
];
97+
nread = wasi::fd_pread(file_fd, iovecs, offset as _).expect("reading bytes at offset 0");
98+
if nread == 0 {
99+
break;
100+
}
101+
contents[offset..offset+nread].copy_from_slice(&buffer[0..nread]);
102+
offset += nread;
103+
}
104+
assert_eq!(offset, 4, "nread bytes check");
105+
assert_eq!(contents, &[0u8, 1, 2, 3], "file cursor was overwritten");
106+
41107
let contents = &mut [0u8; 4];
42108
let iovec = wasi::Iovec {
43109
buf: contents.as_mut_ptr() as *mut _,

crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ unsafe fn test_file_seek_tell(dir_fd: wasi::Fd) {
2525
assert_eq!(offset, 0, "current offset should be 0");
2626

2727
// Write to file
28-
let buf = &[0u8; 100];
28+
let data = &[0u8; 100];
2929
let iov = wasi::Ciovec {
30-
buf: buf.as_ptr() as *const _,
31-
buf_len: buf.len(),
30+
buf: data.as_ptr() as *const _,
31+
buf_len: data.len(),
3232
};
3333
let nwritten = wasi::fd_write(file_fd, &[iov]).expect("writing to a file");
3434
assert_eq!(nwritten, 100, "should write 100 bytes to file");
@@ -65,6 +65,20 @@ unsafe fn test_file_seek_tell(dir_fd: wasi::Fd) {
6565
"errno should be ERRNO_INVAL",
6666
);
6767

68+
// Check that fd_read properly updates the file offset
69+
wasi::fd_seek(file_fd, 0, wasi::WHENCE_SET).expect("seeking to the beginning of the file again");
70+
71+
let buffer = &mut [0u8; 100];
72+
let iovec = wasi::Iovec {
73+
buf: buffer.as_mut_ptr(),
74+
buf_len: buffer.len(),
75+
};
76+
let nread = wasi::fd_read(file_fd, &[iovec]).expect("reading file");
77+
assert_eq!(nread, buffer.len(), "should read {} bytes", buffer.len());
78+
79+
offset = wasi::fd_tell(file_fd).expect("getting file offset after reading");
80+
assert_eq!(offset, 100, "offset after reading should be 100");
81+
6882
wasi::fd_close(file_fd).expect("closing a file");
6983
wasi::path_unlink_file(dir_fd, "file").expect("deleting a file");
7084
}

crates/wasi-common/src/virtfs.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ impl FileContents for VecFileContents {
7878
fn preadv(&self, iovs: &mut [io::IoSliceMut], offset: types::Filesize) -> Result<usize> {
7979
let mut read_total = 0usize;
8080
for iov in iovs.iter_mut() {
81-
let read = self.pread(iov, offset)?;
81+
let skip: u64 = read_total.try_into().map_err(|_| Errno::Inval)?;
82+
let read = self.pread(iov, offset + skip)?;
8283
read_total = read_total.checked_add(read).expect("FileContents::preadv must not be called when reads could total to more bytes than the return value can hold");
8384
}
8485
Ok(read_total)
@@ -87,7 +88,8 @@ impl FileContents for VecFileContents {
8788
fn pwritev(&mut self, iovs: &[io::IoSlice], offset: types::Filesize) -> Result<usize> {
8889
let mut write_total = 0usize;
8990
for iov in iovs.iter() {
90-
let written = self.pwrite(iov, offset)?;
91+
let skip: u64 = write_total.try_into().map_err(|_| Errno::Inval)?;
92+
let written = self.pwrite(iov, offset + skip)?;
9193
write_total = write_total.checked_add(written).expect("FileContents::pwritev must not be called when writes could total to more bytes than the return value can hold");
9294
}
9395
Ok(write_total)
@@ -255,7 +257,11 @@ impl Handle for InMemoryFile {
255257
fn read_vectored(&self, iovs: &mut [io::IoSliceMut]) -> Result<usize> {
256258
trace!("read_vectored(iovs={:?})", iovs);
257259
trace!(" | *read_start={:?}", self.cursor.get());
258-
self.data.borrow_mut().preadv(iovs, self.cursor.get())
260+
let read = self.data.borrow_mut().preadv(iovs, self.cursor.get())?;
261+
let offset: u64 = read.try_into().map_err(|_| Errno::Inval)?;
262+
let update = self.cursor.get().checked_add(offset).ok_or(Errno::Inval)?;
263+
self.cursor.set(update);
264+
Ok(read)
259265
}
260266
fn seek(&self, offset: SeekFrom) -> Result<types::Filesize> {
261267
let content_len = self.data.borrow().size();

0 commit comments

Comments
 (0)