Skip to content

Commit 7f317ca

Browse files
committed
Split unix-specific function into UnixFileDescription
1 parent fefe2dd commit 7f317ca

File tree

7 files changed

+191
-155
lines changed

7 files changed

+191
-155
lines changed

src/shims/files.rs

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,9 @@ use std::rc::{Rc, Weak};
77

88
use rustc_abi::Size;
99

10+
use crate::shims::unix::UnixFileDescription;
1011
use crate::*;
1112

12-
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
13-
pub(crate) enum FlockOp {
14-
SharedLock { nonblocking: bool },
15-
ExclusiveLock { nonblocking: bool },
16-
Unlock,
17-
}
18-
1913
/// Represents an open file description.
2014
pub trait FileDescription: std::fmt::Debug + Any {
2115
fn name(&self) -> &'static str;
@@ -50,37 +44,6 @@ pub trait FileDescription: std::fmt::Debug + Any {
5044
throw_unsup_format!("cannot write to {}", self.name());
5145
}
5246

53-
/// Reads as much as possible into the given buffer `ptr` from a given offset.
54-
/// `len` indicates how many bytes we should try to read.
55-
/// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
56-
fn pread<'tcx>(
57-
&self,
58-
_communicate_allowed: bool,
59-
_offset: u64,
60-
_ptr: Pointer,
61-
_len: usize,
62-
_dest: &MPlaceTy<'tcx>,
63-
_ecx: &mut MiriInterpCx<'tcx>,
64-
) -> InterpResult<'tcx> {
65-
throw_unsup_format!("cannot pread from {}", self.name());
66-
}
67-
68-
/// Writes as much as possible from the given buffer `ptr` starting at a given offset.
69-
/// `ptr` is the pointer to the user supplied read buffer.
70-
/// `len` indicates how many bytes we should try to write.
71-
/// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
72-
fn pwrite<'tcx>(
73-
&self,
74-
_communicate_allowed: bool,
75-
_ptr: Pointer,
76-
_len: usize,
77-
_offset: u64,
78-
_dest: &MPlaceTy<'tcx>,
79-
_ecx: &mut MiriInterpCx<'tcx>,
80-
) -> InterpResult<'tcx> {
81-
throw_unsup_format!("cannot pwrite to {}", self.name());
82-
}
83-
8447
/// Seeks to the given offset (which can be relative to the beginning, end, or current position).
8548
/// Returns the new position from the start of the stream.
8649
fn seek<'tcx>(
@@ -99,25 +62,14 @@ pub trait FileDescription: std::fmt::Debug + Any {
9962
throw_unsup_format!("cannot close {}", self.name());
10063
}
10164

102-
fn flock<'tcx>(
103-
&self,
104-
_communicate_allowed: bool,
105-
_op: FlockOp,
106-
) -> InterpResult<'tcx, io::Result<()>> {
107-
throw_unsup_format!("cannot flock {}", self.name());
108-
}
109-
11065
fn is_tty(&self, _communicate_allowed: bool) -> bool {
11166
// Most FDs are not tty's and the consequence of a wrong `false` are minor,
11267
// so we use a default impl here.
11368
false
11469
}
11570

116-
/// Check the readiness of file description.
117-
fn get_epoll_ready_events<'tcx>(
118-
&self,
119-
) -> InterpResult<'tcx, crate::shims::unix::linux::epoll::EpollReadyEvents> {
120-
throw_unsup_format!("{}: epoll does not support this file description", self.name());
71+
fn as_unix<'tcx>(&self) -> InterpResult<'tcx, &dyn UnixFileDescription> {
72+
throw_unsup_format!("Not a unix file descriptor: {}", self.name());
12173
}
12274
}
12375

src/shims/unix/fd.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,71 @@
11
//! General management of file descriptors, and support for
22
//! standard file descriptors (stdin/stdout/stderr).
33
4+
use std::io;
45
use std::io::ErrorKind;
56

67
use rustc_abi::Size;
78

89
use crate::helpers::check_min_arg_count;
9-
use crate::shims::files::FlockOp;
10+
use crate::shims::files::FileDescription;
11+
use crate::shims::unix::linux::epoll::EpollReadyEvents;
1012
use crate::shims::unix::*;
1113
use crate::*;
1214

15+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
16+
pub(crate) enum FlockOp {
17+
SharedLock { nonblocking: bool },
18+
ExclusiveLock { nonblocking: bool },
19+
Unlock,
20+
}
21+
22+
/// Represents unix-specific file descriptions.
23+
pub trait UnixFileDescription: FileDescription {
24+
/// Reads as much as possible into the given buffer `ptr` from a given offset.
25+
/// `len` indicates how many bytes we should try to read.
26+
/// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
27+
fn pread<'tcx>(
28+
&self,
29+
_communicate_allowed: bool,
30+
_offset: u64,
31+
_ptr: Pointer,
32+
_len: usize,
33+
_dest: &MPlaceTy<'tcx>,
34+
_ecx: &mut MiriInterpCx<'tcx>,
35+
) -> InterpResult<'tcx> {
36+
throw_unsup_format!("cannot pread from {}", self.name());
37+
}
38+
39+
/// Writes as much as possible from the given buffer `ptr` starting at a given offset.
40+
/// `ptr` is the pointer to the user supplied read buffer.
41+
/// `len` indicates how many bytes we should try to write.
42+
/// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
43+
fn pwrite<'tcx>(
44+
&self,
45+
_communicate_allowed: bool,
46+
_ptr: Pointer,
47+
_len: usize,
48+
_offset: u64,
49+
_dest: &MPlaceTy<'tcx>,
50+
_ecx: &mut MiriInterpCx<'tcx>,
51+
) -> InterpResult<'tcx> {
52+
throw_unsup_format!("cannot pwrite to {}", self.name());
53+
}
54+
55+
fn flock<'tcx>(
56+
&self,
57+
_communicate_allowed: bool,
58+
_op: FlockOp,
59+
) -> InterpResult<'tcx, io::Result<()>> {
60+
throw_unsup_format!("cannot flock {}", self.name());
61+
}
62+
63+
/// Check the readiness of file description.
64+
fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
65+
throw_unsup_format!("{}: epoll does not support this file description", self.name());
66+
}
67+
}
68+
1369
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1470
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1571
fn dup(&mut self, old_fd_num: i32) -> InterpResult<'tcx, Scalar> {
@@ -65,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
65121
throw_unsup_format!("unsupported flags {:#x}", op);
66122
};
67123

68-
let result = fd.flock(this.machine.communicate(), parsed_op)?;
124+
let result = fd.as_unix()?.flock(this.machine.communicate(), parsed_op)?;
69125
drop(fd);
70126
// return `0` if flock is successful
71127
let result = result.map(|()| 0i32);
@@ -195,7 +251,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
195251
let Ok(offset) = u64::try_from(offset) else {
196252
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
197253
};
198-
fd.pread(communicate, offset, buf, count, dest, this)?
254+
fd.as_unix()?.pread(communicate, offset, buf, count, dest, this)?
199255
}
200256
};
201257
interp_ok(())
@@ -235,7 +291,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
235291
let Ok(offset) = u64::try_from(offset) else {
236292
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
237293
};
238-
fd.pwrite(communicate, buf, count, offset, dest, this)?
294+
fd.as_unix()?.pwrite(communicate, buf, count, offset, dest, this)?
239295
}
240296
};
241297
interp_ok(())

src/shims/unix/fs.rs

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ use rustc_data_structures::fx::FxHashMap;
1313

1414
use self::shims::time::system_time_to_duration;
1515
use crate::helpers::check_min_arg_count;
16-
use crate::shims::files::{EvalContextExt as _, FileDescription, FileDescriptionRef, FlockOp};
16+
use crate::shims::files::{EvalContextExt as _, FileDescription, FileDescriptionRef};
1717
use crate::shims::os_str::bytes_to_os_str;
18+
use crate::shims::unix::fd::{FlockOp, UnixFileDescription};
1819
use crate::*;
1920

2021
#[derive(Debug)]
@@ -64,6 +65,51 @@ impl FileDescription for FileHandle {
6465
}
6566
}
6667

68+
fn seek<'tcx>(
69+
&self,
70+
communicate_allowed: bool,
71+
offset: SeekFrom,
72+
) -> InterpResult<'tcx, io::Result<u64>> {
73+
assert!(communicate_allowed, "isolation should have prevented even opening a file");
74+
interp_ok((&mut &self.file).seek(offset))
75+
}
76+
77+
fn close<'tcx>(
78+
self: Box<Self>,
79+
communicate_allowed: bool,
80+
_ecx: &mut MiriInterpCx<'tcx>,
81+
) -> InterpResult<'tcx, io::Result<()>> {
82+
assert!(communicate_allowed, "isolation should have prevented even opening a file");
83+
// We sync the file if it was opened in a mode different than read-only.
84+
if self.writable {
85+
// `File::sync_all` does the checks that are done when closing a file. We do this to
86+
// to handle possible errors correctly.
87+
let result = self.file.sync_all();
88+
// Now we actually close the file and return the result.
89+
drop(*self);
90+
interp_ok(result)
91+
} else {
92+
// We drop the file, this closes it but ignores any errors
93+
// produced when closing it. This is done because
94+
// `File::sync_all` cannot be done over files like
95+
// `/dev/urandom` which are read-only. Check
96+
// https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
97+
// for a deeper discussion.
98+
drop(*self);
99+
interp_ok(Ok(()))
100+
}
101+
}
102+
103+
fn is_tty(&self, communicate_allowed: bool) -> bool {
104+
communicate_allowed && self.file.is_terminal()
105+
}
106+
107+
fn as_unix<'tcx>(&self) -> InterpResult<'tcx, &dyn UnixFileDescription> {
108+
interp_ok(self)
109+
}
110+
}
111+
112+
impl UnixFileDescription for FileHandle {
67113
fn pread<'tcx>(
68114
&self,
69115
communicate_allowed: bool,
@@ -126,41 +172,6 @@ impl FileDescription for FileHandle {
126172
}
127173
}
128174

129-
fn seek<'tcx>(
130-
&self,
131-
communicate_allowed: bool,
132-
offset: SeekFrom,
133-
) -> InterpResult<'tcx, io::Result<u64>> {
134-
assert!(communicate_allowed, "isolation should have prevented even opening a file");
135-
interp_ok((&mut &self.file).seek(offset))
136-
}
137-
138-
fn close<'tcx>(
139-
self: Box<Self>,
140-
communicate_allowed: bool,
141-
_ecx: &mut MiriInterpCx<'tcx>,
142-
) -> InterpResult<'tcx, io::Result<()>> {
143-
assert!(communicate_allowed, "isolation should have prevented even opening a file");
144-
// We sync the file if it was opened in a mode different than read-only.
145-
if self.writable {
146-
// `File::sync_all` does the checks that are done when closing a file. We do this to
147-
// to handle possible errors correctly.
148-
let result = self.file.sync_all();
149-
// Now we actually close the file and return the result.
150-
drop(*self);
151-
interp_ok(result)
152-
} else {
153-
// We drop the file, this closes it but ignores any errors
154-
// produced when closing it. This is done because
155-
// `File::sync_all` cannot be done over files like
156-
// `/dev/urandom` which are read-only. Check
157-
// https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
158-
// for a deeper discussion.
159-
drop(*self);
160-
interp_ok(Ok(()))
161-
}
162-
}
163-
164175
fn flock<'tcx>(
165176
&self,
166177
communicate_allowed: bool,
@@ -255,10 +266,6 @@ impl FileDescription for FileHandle {
255266
compile_error!("flock is supported only on UNIX and Windows hosts");
256267
}
257268
}
258-
259-
fn is_tty(&self, communicate_allowed: bool) -> bool {
260-
communicate_allowed && self.file.is_terminal()
261-
}
262269
}
263270

264271
impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {}

src/shims/unix/linux/epoll.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::time::Duration;
66

77
use crate::concurrency::VClock;
88
use crate::shims::files::{FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef};
9+
use crate::shims::unix::UnixFileDescription;
910
use crate::*;
1011

1112
/// An `Epoll` file descriptor connects file handles and epoll events
@@ -151,8 +152,14 @@ impl FileDescription for Epoll {
151152
) -> InterpResult<'tcx, io::Result<()>> {
152153
interp_ok(Ok(()))
153154
}
155+
156+
fn as_unix<'tcx>(&self) -> InterpResult<'tcx, &dyn UnixFileDescription> {
157+
interp_ok(self)
158+
}
154159
}
155160

161+
impl UnixFileDescription for Epoll {}
162+
156163
/// The table of all EpollEventInterest.
157164
/// The BTreeMap key is the FdId of an active file description registered with
158165
/// any epoll instance. The value is a list of EpollEventInterest associated
@@ -594,7 +601,7 @@ fn check_and_update_one_event_interest<'tcx>(
594601
ecx: &MiriInterpCx<'tcx>,
595602
) -> InterpResult<'tcx, bool> {
596603
// Get the bitmask of ready events for a file description.
597-
let ready_events_bitmask = fd_ref.get_epoll_ready_events()?.get_event_bitmask(ecx);
604+
let ready_events_bitmask = fd_ref.as_unix()?.get_epoll_ready_events()?.get_event_bitmask(ecx);
598605
let epoll_event_interest = interest.borrow();
599606
// This checks if any of the events specified in epoll_event_interest.events
600607
// match those in ready_events.

src/shims/unix/linux/eventfd.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::io::ErrorKind;
55

66
use crate::concurrency::VClock;
77
use crate::shims::files::{FileDescription, FileDescriptionRef, WeakFileDescriptionRef};
8+
use crate::shims::unix::UnixFileDescription;
89
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
910
use crate::*;
1011

@@ -36,17 +37,6 @@ impl FileDescription for Event {
3637
"event"
3738
}
3839

39-
fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
40-
// We only check the status of EPOLLIN and EPOLLOUT flags for eventfd. If other event flags
41-
// need to be supported in the future, the check should be added here.
42-
43-
interp_ok(EpollReadyEvents {
44-
epollin: self.counter.get() != 0,
45-
epollout: self.counter.get() != MAX_COUNTER,
46-
..EpollReadyEvents::new()
47-
})
48-
}
49-
5040
fn close<'tcx>(
5141
self: Box<Self>,
5242
_communicate_allowed: bool,
@@ -120,6 +110,23 @@ impl FileDescription for Event {
120110
let weak_eventfd = self_ref.downgrade();
121111
eventfd_write(num, buf_place, dest, weak_eventfd, ecx)
122112
}
113+
114+
fn as_unix<'tcx>(&self) -> InterpResult<'tcx, &dyn UnixFileDescription> {
115+
interp_ok(self)
116+
}
117+
}
118+
119+
impl UnixFileDescription for Event {
120+
fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
121+
// We only check the status of EPOLLIN and EPOLLOUT flags for eventfd. If other event flags
122+
// need to be supported in the future, the check should be added here.
123+
124+
interp_ok(EpollReadyEvents {
125+
epollin: self.counter.get() != 0,
126+
epollout: self.counter.get() != MAX_COUNTER,
127+
..EpollReadyEvents::new()
128+
})
129+
}
123130
}
124131

125132
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}

src/shims/unix/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ mod unnamed_socket;
1010

1111
mod android;
1212
mod freebsd;
13-
pub mod linux;
13+
mod linux;
1414
mod macos;
1515
mod solarish;
1616

1717
// All the Unix-specific extension traits
1818
pub use self::env::{EvalContextExt as _, UnixEnvVars};
19-
pub use self::fd::EvalContextExt as _;
19+
pub use self::fd::{EvalContextExt as _, UnixFileDescription};
2020
pub use self::fs::{DirTable, EvalContextExt as _};
2121
pub use self::linux::epoll::EpollInterestTable;
2222
pub use self::mem::EvalContextExt as _;

0 commit comments

Comments
 (0)