Skip to content

Commit 1151e0d

Browse files
committed
Refactor FileDescription::read/write helper
1 parent cf318e7 commit 1151e0d

File tree

4 files changed

+69
-60
lines changed

4 files changed

+69
-60
lines changed

src/shims/unix/fd.rs

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ impl FileDescription for io::Stdin {
151151
helpers::isolation_abort_error("`read` from stdin")?;
152152
}
153153
let result = Read::read(&mut { self }, &mut bytes);
154-
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
154+
match result {
155+
Ok(rw_bytes) =>
156+
ecx.return_read_write_success(Some(ptr), Some(&bytes), true, rw_bytes, dest),
157+
Err(e) => ecx.return_read_write_error(e, dest),
158+
}
155159
}
156160

157161
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -182,7 +186,10 @@ impl FileDescription for io::Stdout {
182186
// the host -- there is no good in adding extra buffering
183187
// here.
184188
io::stdout().flush().unwrap();
185-
ecx.return_written_byte_count_or_error(result, dest)
189+
match result {
190+
Ok(rw_bytes) => ecx.return_read_write_success(None, None, false, rw_bytes, dest),
191+
Err(e) => ecx.return_read_write_error(e, dest),
192+
}
186193
}
187194

188195
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -208,7 +215,10 @@ impl FileDescription for io::Stderr {
208215
// We allow writing to stderr even with isolation enabled.
209216
// No need to flush, stderr is not buffered.
210217
let result = Write::write(&mut { self }, bytes);
211-
ecx.return_written_byte_count_or_error(result, dest)
218+
match result {
219+
Ok(rw_bytes) => ecx.return_read_write_success(None, None, false, rw_bytes, dest),
220+
Err(e) => ecx.return_read_write_error(e, dest),
221+
}
212222
}
213223

214224
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -235,8 +245,7 @@ impl FileDescription for NullOutput {
235245
ecx: &mut MiriInterpCx<'tcx>,
236246
) -> InterpResult<'tcx> {
237247
// We just don't write anything, but report to the user that we did.
238-
let result = Ok(len);
239-
ecx.return_written_byte_count_or_error(result, dest)
248+
ecx.return_read_write_success(None, None, false, len, dest)
240249
}
241250
}
242251

@@ -655,47 +664,44 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
655664
Ok(())
656665
}
657666

658-
/// Helper to implement `FileDescription::read`:
659-
/// `result` should be the return value of some underlying `read` call that used `bytes` as its output buffer.
667+
/// Helper to implement `FileDescription::read/write`:
660668
/// The length of `bytes` must not exceed either the host's or the target's `isize`.
661-
/// If `Result` indicates success, `bytes` is written to `buf` and the size is written to `dest`.
662-
/// Otherwise, `-1` is written to `dest` and the last libc error is set appropriately.
663-
fn return_read_bytes_and_count(
669+
/// `is_read` is used to indicate if this helper is used for `read`.
670+
/// If it is `read`, `bytes` is written to `buf`.
671+
/// For both `read` and `write`,`actual_rw_size`, which is the actual read/write size,
672+
/// is written to `dest`.
673+
fn return_read_write_success(
664674
&mut self,
665-
buf: Pointer,
666-
bytes: &[u8],
667-
result: io::Result<usize>,
675+
buf: Option<Pointer>,
676+
bytes: Option<&[u8]>,
677+
is_read: bool,
678+
actual_rw_size: usize,
668679
dest: &MPlaceTy<'tcx>,
669680
) -> InterpResult<'tcx> {
670681
let this = self.eval_context_mut();
671-
match result {
672-
Ok(read_bytes) => {
673-
// If reading to `bytes` did not fail, we write those bytes to the buffer.
674-
// Crucially, if fewer than `bytes.len()` bytes were read, only write
675-
// that much into the output buffer!
676-
this.write_bytes_ptr(buf, bytes[..read_bytes].iter().copied())?;
677-
// The actual read size is always less than what got originally requested so this cannot fail.
678-
this.write_int(u64::try_from(read_bytes).unwrap(), dest)?;
679-
Ok(())
680-
}
681-
Err(e) => {
682-
this.set_last_error_from_io_error(e)?;
683-
this.write_int(-1, dest)?;
684-
Ok(())
685-
}
682+
if is_read {
683+
let bytes = bytes.unwrap();
684+
// If reading to `bytes` did not fail, we write those bytes to the buffer.
685+
// Crucially, if fewer than `bytes.len()` bytes were read, only write
686+
// that much into the output buffer!
687+
this.write_bytes_ptr(buf.unwrap(), bytes[..actual_rw_size].iter().copied())?;
686688
}
689+
// The actual read/write size is always less than what got originally requested so this cannot fail.
690+
this.write_int(u64::try_from(actual_rw_size).unwrap(), dest)?;
691+
Ok(())
687692
}
688693

689-
/// This function writes the number of written bytes (given in `result`) to `dest`, or sets the
690-
/// last libc error and writes -1 to dest.
691-
fn return_written_byte_count_or_error(
694+
/// Helper to implement `FileDescription::read/write`.
695+
/// This is only used when there is a read/write error.
696+
///`-1` is written to `dest` and the last libc error is set appropriately.
697+
fn return_read_write_error(
692698
&mut self,
693-
result: io::Result<usize>,
699+
err: io::Error,
694700
dest: &MPlaceTy<'tcx>,
695701
) -> InterpResult<'tcx> {
696702
let this = self.eval_context_mut();
697-
let result = this.try_unwrap_io_result(result.map(|c| i64::try_from(c).unwrap()))?;
698-
this.write_int(result, dest)?;
703+
this.set_last_error(this.io_error_to_errnum(err)?)?;
704+
this.write_int(-1, dest)?;
699705
Ok(())
700706
}
701707
}

src/shims/unix/fs.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ impl FileDescription for FileHandle {
4242
assert!(communicate_allowed, "isolation should have prevented even opening a file");
4343
let mut bytes = vec![0; len];
4444
let result = (&mut &self.file).read(&mut bytes);
45-
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
45+
match result {
46+
Ok(rw_bytes) =>
47+
ecx.return_read_write_success(Some(ptr), Some(&bytes), true, rw_bytes, dest),
48+
Err(e) => ecx.return_read_write_error(e, dest),
49+
}
4650
}
4751

4852
fn write<'tcx>(
@@ -57,7 +61,10 @@ impl FileDescription for FileHandle {
5761
assert!(communicate_allowed, "isolation should have prevented even opening a file");
5862
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
5963
let result = (&mut &self.file).write(bytes);
60-
ecx.return_written_byte_count_or_error(result, dest)
64+
match result {
65+
Ok(rw_bytes) => ecx.return_read_write_success(None, None, false, rw_bytes, dest),
66+
Err(e) => ecx.return_read_write_error(e, dest),
67+
}
6168
}
6269

6370
fn pread<'tcx>(
@@ -85,7 +92,11 @@ impl FileDescription for FileHandle {
8592
res
8693
};
8794
let result = f();
88-
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
95+
match result {
96+
Ok(rw_bytes) =>
97+
ecx.return_read_write_success(Some(ptr), Some(&bytes), true, rw_bytes, dest),
98+
Err(e) => ecx.return_read_write_error(e, dest),
99+
}
89100
}
90101

91102
fn pwrite<'tcx>(
@@ -113,7 +124,10 @@ impl FileDescription for FileHandle {
113124
res
114125
};
115126
let result = f();
116-
ecx.return_written_byte_count_or_error(result, dest)
127+
match result {
128+
Ok(rw_bytes) => ecx.return_read_write_success(None, None, false, rw_bytes, dest),
129+
Err(e) => ecx.return_read_write_error(e, dest),
130+
}
117131
}
118132

119133
fn seek<'tcx>(

src/shims/unix/linux/eventfd.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,7 @@ impl FileDescription for Event {
127127
let ty = ecx.machine.layouts.u64;
128128
// Check the size of slice, and return error only if the size of the slice < 8.
129129
if len < ty.layout.size.bytes_usize() {
130-
let result = Err(Error::from(ErrorKind::InvalidInput));
131-
return ecx.return_written_byte_count_or_error(result, dest);
130+
return ecx.return_read_write_error(Error::from(ErrorKind::InvalidInput), dest);
132131
}
133132

134133
// Read the user supplied value from the pointer.
@@ -137,8 +136,7 @@ impl FileDescription for Event {
137136

138137
// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
139138
if num == u64::MAX {
140-
let result = Err(Error::from(ErrorKind::InvalidInput));
141-
return ecx.return_written_byte_count_or_error(result, dest);
139+
return ecx.return_read_write_error(Error::from(ErrorKind::InvalidInput), dest);
142140
}
143141
// If the addition does not let the counter to exceed the maximum value, update the counter.
144142
// Else, block.
@@ -152,8 +150,7 @@ impl FileDescription for Event {
152150
}
153151
None | Some(u64::MAX) =>
154152
if self.is_nonblock {
155-
let result = Err(Error::from(ErrorKind::WouldBlock));
156-
return ecx.return_written_byte_count_or_error(result, dest);
153+
return ecx.return_read_write_error(Error::from(ErrorKind::WouldBlock), dest);
157154
} else {
158155
throw_unsup_format!("eventfd: blocking is unsupported");
159156
},

src/shims/unix/unnamed_socket.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ impl FileDescription for AnonSocket {
137137

138138
// Always succeed on read size 0.
139139
if len == 0 {
140-
let result = Ok(0);
141-
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
140+
return ecx.return_read_write_success(Some(ptr), Some(&bytes), true, 0, dest);
142141
}
143142

144143
let Some(readbuf) = &self.readbuf else {
@@ -151,17 +150,15 @@ impl FileDescription for AnonSocket {
151150
if self.peer_fd().upgrade().is_none() {
152151
// Socketpair with no peer and empty buffer.
153152
// 0 bytes successfully read indicates end-of-file.
154-
let result = Ok(0);
155-
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
153+
return ecx.return_read_write_success(Some(ptr), Some(&bytes), true, 0, dest);
156154
} else {
157155
if self.is_nonblock {
158156
// Non-blocking socketpair with writer and empty buffer.
159157
// https://linux.die.net/man/2/read
160158
// EAGAIN or EWOULDBLOCK can be returned for socket,
161159
// POSIX.1-2001 allows either error to be returned for this case.
162160
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
163-
let result = Err(Error::from(ErrorKind::WouldBlock));
164-
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
161+
return ecx.return_read_write_error(Error::from(ErrorKind::WouldBlock), dest);
165162
} else {
166163
// Blocking socketpair with writer and empty buffer.
167164
// FIXME: blocking is currently not supported
@@ -193,8 +190,7 @@ impl FileDescription for AnonSocket {
193190
ecx.check_and_update_readiness(&peer_fd)?;
194191
}
195192

196-
let result = Ok(actual_read_size);
197-
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
193+
ecx.return_read_write_success(Some(ptr), Some(&bytes), true, actual_read_size, dest)
198194
}
199195

200196
fn write<'tcx>(
@@ -209,16 +205,14 @@ impl FileDescription for AnonSocket {
209205
// Always succeed on write size 0.
210206
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
211207
if len == 0 {
212-
let result = Ok(0);
213-
return ecx.return_written_byte_count_or_error(result, dest);
208+
return ecx.return_read_write_success(None, None, false, 0, dest);
214209
}
215210

216211
// We are writing to our peer's readbuf.
217212
let Some(peer_fd) = self.peer_fd().upgrade() else {
218213
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
219214
// closed.
220-
let result = Err(Error::from(ErrorKind::BrokenPipe));
221-
return ecx.return_written_byte_count_or_error(result, dest);
215+
return ecx.return_read_write_error(Error::from(ErrorKind::BrokenPipe), dest);
222216
};
223217

224218
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
@@ -232,8 +226,7 @@ impl FileDescription for AnonSocket {
232226
if available_space == 0 {
233227
if self.is_nonblock {
234228
// Non-blocking socketpair with a full buffer.
235-
let result = Err(Error::from(ErrorKind::WouldBlock));
236-
return ecx.return_written_byte_count_or_error(result, dest);
229+
return ecx.return_read_write_error(Error::from(ErrorKind::WouldBlock), dest);
237230
} else {
238231
// Blocking socketpair with a full buffer.
239232
throw_unsup_format!("socketpair write: blocking isn't supported yet");
@@ -255,8 +248,7 @@ impl FileDescription for AnonSocket {
255248
// The kernel does this even if the fd was already readable before, so we follow suit.
256249
ecx.check_and_update_readiness(&peer_fd)?;
257250

258-
let result = Ok(actual_write_size);
259-
ecx.return_written_byte_count_or_error(result, dest)
251+
ecx.return_read_write_success(None, None, false, actual_write_size, dest)
260252
}
261253
}
262254

0 commit comments

Comments
 (0)