hermit/fd/
mod.rs

1use alloc::boxed::Box;
2use alloc::sync::Arc;
3use alloc::vec::Vec;
4use core::future::{self, Future};
5use core::mem::MaybeUninit;
6use core::task::Poll::{Pending, Ready};
7use core::time::Duration;
8
9use async_trait::async_trait;
10#[cfg(any(feature = "tcp", feature = "udp"))]
11use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
12
13use crate::arch::kernel::core_local::core_scheduler;
14use crate::executor::block_on;
15use crate::fs::{DirectoryEntry, FileAttr, SeekWhence};
16use crate::io;
17
18mod eventfd;
19#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
20pub(crate) mod socket;
21pub(crate) mod stdio;
22
23pub(crate) const STDIN_FILENO: FileDescriptor = 0;
24pub(crate) const STDOUT_FILENO: FileDescriptor = 1;
25pub(crate) const STDERR_FILENO: FileDescriptor = 2;
26
27#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
28#[derive(Debug)]
29pub(crate) enum Endpoint {
30	#[cfg(any(feature = "tcp", feature = "udp"))]
31	Ip(IpEndpoint),
32	#[cfg(feature = "vsock")]
33	Vsock(socket::vsock::VsockEndpoint),
34}
35
36#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
37#[derive(Debug)]
38pub(crate) enum ListenEndpoint {
39	#[cfg(any(feature = "tcp", feature = "udp"))]
40	Ip(IpListenEndpoint),
41	#[cfg(feature = "vsock")]
42	Vsock(socket::vsock::VsockListenEndpoint),
43}
44
45#[allow(dead_code)]
46#[derive(Debug, PartialEq)]
47pub(crate) enum SocketOption {
48	TcpNoDelay,
49}
50
51pub(crate) type FileDescriptor = i32;
52
53bitflags! {
54	/// Options for opening files
55	#[derive(Debug, Copy, Clone, Default)]
56	pub struct OpenOption: i32 {
57		const O_RDONLY = 0o0000;
58		const O_WRONLY = 0o0001;
59		const O_RDWR = 0o0002;
60		const O_CREAT = 0o0100;
61		const O_EXCL = 0o0200;
62		const O_TRUNC = 0o1000;
63		const O_APPEND = StatusFlags::O_APPEND.bits();
64		const O_NONBLOCK = StatusFlags::O_NONBLOCK.bits();
65		const O_DIRECT = 0o40000;
66		const O_DIRECTORY = 0o200_000;
67		/// `O_CLOEXEC` has no functionality in Hermit and will be silently ignored
68		const O_CLOEXEC = 0o2_000_000;
69	}
70}
71
72bitflags! {
73	/// File status flags.
74	#[derive(Debug, Copy, Clone, Default)]
75	pub struct StatusFlags: i32 {
76		const O_APPEND = 0o2000;
77		const O_NONBLOCK = 0o4000;
78	}
79}
80
81bitflags! {
82	#[derive(Debug, Copy, Clone, Default)]
83	pub struct PollEvent: i16 {
84		const POLLIN = 0x1;
85		const POLLPRI = 0x2;
86		const POLLOUT = 0x4;
87		const POLLERR = 0x8;
88		const POLLHUP = 0x10;
89		const POLLNVAL = 0x20;
90		const POLLRDNORM = 0x040;
91		const POLLRDBAND = 0x080;
92		const POLLWRNORM = 0x0100;
93		const POLLWRBAND = 0x0200;
94		const POLLRDHUP = 0x2000;
95	}
96}
97
98#[repr(C)]
99#[derive(Debug, Default, Copy, Clone)]
100pub struct PollFd {
101	/// file descriptor
102	pub fd: i32,
103	/// events to look for
104	pub events: PollEvent,
105	/// events returned
106	pub revents: PollEvent,
107}
108
109bitflags! {
110	#[derive(Debug, Default, Copy, Clone)]
111	pub struct EventFlags: i16 {
112		const EFD_SEMAPHORE = 0o1;
113		const EFD_NONBLOCK = 0o4000;
114		const EFD_CLOEXEC = 0o40000;
115	}
116}
117
118bitflags! {
119	#[derive(Debug, Copy, Clone)]
120	pub struct AccessPermission: u32 {
121		const S_IFMT = 0o170_000;
122		const S_IFSOCK = 0o140_000;
123		const S_IFLNK = 0o120_000;
124		const S_IFREG = 0o100_000;
125		const S_IFBLK = 0o060_000;
126		const S_IFDIR = 0o040_000;
127		const S_IFCHR = 0o020_000;
128		const S_IFIFO = 0o010_000;
129		const S_IRUSR = 0o400;
130		const S_IWUSR = 0o200;
131		const S_IXUSR = 0o100;
132		const S_IRWXU = 0o700;
133		const S_IRGRP = 0o040;
134		const S_IWGRP = 0o020;
135		const S_IXGRP = 0o010;
136		const S_IRWXG = 0o070;
137		const S_IROTH = 0o004;
138		const S_IWOTH = 0o002;
139		const S_IXOTH = 0o001;
140		const S_IRWXO = 0o007;
141		// Allow bits unknown to us to be set externally. See bitflags documentation for further explanation.
142		const _ = !0;
143	}
144}
145
146impl Default for AccessPermission {
147	fn default() -> Self {
148		AccessPermission::from_bits(0o666).unwrap()
149	}
150}
151
152#[async_trait]
153pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug {
154	/// check if an IO event is possible
155	async fn poll(&self, _event: PollEvent) -> io::Result<PollEvent> {
156		Ok(PollEvent::empty())
157	}
158
159	/// `async_read` attempts to read `len` bytes from the object references
160	/// by the descriptor
161	async fn read(&self, _buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
162		Err(io::Error::ENOSYS)
163	}
164
165	/// `async_write` attempts to write `len` bytes to the object references
166	/// by the descriptor
167	async fn write(&self, _buf: &[u8]) -> io::Result<usize> {
168		Err(io::Error::ENOSYS)
169	}
170
171	/// `lseek` function repositions the offset of the file descriptor fildes
172	async fn lseek(&self, _offset: isize, _whence: SeekWhence) -> io::Result<isize> {
173		Err(io::Error::EINVAL)
174	}
175
176	/// `fstat`
177	async fn fstat(&self) -> io::Result<FileAttr> {
178		Err(io::Error::EINVAL)
179	}
180
181	/// 'readdir' returns a pointer to a dirent structure
182	/// representing the next directory entry in the directory stream
183	/// pointed to by the file descriptor
184	async fn readdir(&self) -> io::Result<Vec<DirectoryEntry>> {
185		Err(io::Error::EINVAL)
186	}
187
188	/// `accept` a connection on a socket
189	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
190	async fn accept(&self) -> io::Result<(Arc<dyn ObjectInterface>, Endpoint)> {
191		Err(io::Error::EINVAL)
192	}
193
194	/// initiate a connection on a socket
195	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
196	async fn connect(&self, _endpoint: Endpoint) -> io::Result<()> {
197		Err(io::Error::EINVAL)
198	}
199
200	/// `bind` a name to a socket
201	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
202	async fn bind(&self, _name: ListenEndpoint) -> io::Result<()> {
203		Err(io::Error::EINVAL)
204	}
205
206	/// `listen` for connections on a socket
207	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
208	async fn listen(&self, _backlog: i32) -> io::Result<()> {
209		Err(io::Error::EINVAL)
210	}
211
212	/// `setsockopt` sets options on sockets
213	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
214	async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()> {
215		Err(io::Error::ENOTSOCK)
216	}
217
218	/// `getsockopt` gets options on sockets
219	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
220	async fn getsockopt(&self, _opt: SocketOption) -> io::Result<bool> {
221		Err(io::Error::ENOTSOCK)
222	}
223
224	/// `getsockname` gets socket name
225	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
226	async fn getsockname(&self) -> io::Result<Option<Endpoint>> {
227		Ok(None)
228	}
229
230	/// `getpeername` get address of connected peer
231	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
232	#[allow(dead_code)]
233	async fn getpeername(&self) -> io::Result<Option<Endpoint>> {
234		Ok(None)
235	}
236
237	/// receive a message from a socket
238	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
239	async fn recvfrom(&self, _buffer: &mut [MaybeUninit<u8>]) -> io::Result<(usize, Endpoint)> {
240		Err(io::Error::ENOSYS)
241	}
242
243	/// send a message from a socket
244	///
245	/// The sendto() function shall send a message.
246	/// If the socket is a connectionless-mode socket, the message shall
247	/// If a peer address has been prespecified, either the message shall
248	/// be sent to the address specified by dest_addr (overriding the pre-specified peer
249	/// address).
250	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
251	async fn sendto(&self, _buffer: &[u8], _endpoint: Endpoint) -> io::Result<usize> {
252		Err(io::Error::ENOSYS)
253	}
254
255	/// shut down part of a full-duplex connection
256	#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
257	async fn shutdown(&self, _how: i32) -> io::Result<()> {
258		Err(io::Error::ENOSYS)
259	}
260
261	/// Returns the file status flags.
262	async fn status_flags(&self) -> io::Result<StatusFlags> {
263		Err(io::Error::ENOSYS)
264	}
265
266	/// Sets the file status flags.
267	async fn set_status_flags(&self, _status_flags: StatusFlags) -> io::Result<()> {
268		Err(io::Error::ENOSYS)
269	}
270
271	/// `isatty` returns `true` for a terminal device
272	async fn isatty(&self) -> io::Result<bool> {
273		Ok(false)
274	}
275
276	// FIXME: remove once the ecosystem has migrated away from `AF_INET_OLD`
277	#[cfg(any(feature = "tcp", feature = "udp"))]
278	async fn inet_domain(&self) -> io::Result<i32> {
279		Err(io::Error::EINVAL)
280	}
281}
282
283pub(crate) fn read(fd: FileDescriptor, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
284	let obj = get_object(fd)?;
285
286	if buf.is_empty() {
287		return Ok(0);
288	}
289
290	block_on(obj.read(buf), None)
291}
292
293pub(crate) fn lseek(fd: FileDescriptor, offset: isize, whence: SeekWhence) -> io::Result<isize> {
294	let obj = get_object(fd)?;
295
296	block_on(obj.lseek(offset, whence), None)
297}
298
299pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> io::Result<usize> {
300	let obj = get_object(fd)?;
301
302	if buf.is_empty() {
303		return Ok(0);
304	}
305
306	block_on(obj.write(buf), None)
307}
308
309async fn poll_fds(fds: &mut [PollFd]) -> io::Result<u64> {
310	future::poll_fn(|cx| {
311		let mut counter: u64 = 0;
312
313		for i in &mut *fds {
314			let fd = i.fd;
315			i.revents = PollEvent::empty();
316			let mut pinned_obj = core::pin::pin!(core_scheduler().get_object(fd));
317			if let Ready(Ok(obj)) = pinned_obj.as_mut().poll(cx) {
318				let mut pinned = core::pin::pin!(obj.poll(i.events));
319				if let Ready(Ok(e)) = pinned.as_mut().poll(cx) {
320					if !e.is_empty() {
321						counter += 1;
322						i.revents = e;
323					}
324				}
325			}
326		}
327
328		if counter > 0 {
329			Ready(Ok(counter))
330		} else {
331			Pending
332		}
333	})
334	.await
335}
336
337/// Wait for some event on a file descriptor.
338///
339/// The unix-like `poll` waits for one of a set of file descriptors
340/// to become ready to perform I/O. The set of file descriptors to be
341/// monitored is specified in the `fds` argument, which is an array
342/// of structs of `PollFd`.
343pub fn poll(fds: &mut [PollFd], timeout: Option<Duration>) -> io::Result<u64> {
344	let result = block_on(poll_fds(fds), timeout);
345	if let Err(ref e) = result {
346		if timeout.is_some() {
347			// A return value of zero indicates that the system call timed out
348			if *e == io::Error::EAGAIN {
349				return Ok(0);
350			}
351		}
352	}
353
354	result
355}
356
357pub fn fstat(fd: FileDescriptor) -> io::Result<FileAttr> {
358	let obj = get_object(fd)?;
359	block_on(obj.fstat(), None)
360}
361
362/// Wait for some event on a file descriptor.
363///
364/// `eventfd` creates an linux-like "eventfd object" that can be used
365/// as an event wait/notify mechanism by user-space applications, and by
366/// the kernel to notify user-space applications of events. The
367/// object contains an unsigned 64-bit integer counter
368/// that is maintained by the kernel. This counter is initialized
369/// with the value specified in the argument `initval`.
370///
371/// As its return value, `eventfd` returns a new file descriptor that
372/// can be used to refer to the eventfd object.
373///
374/// The following values may be bitwise set in flags to change the
375/// behavior of `eventfd`:
376///
377/// `EFD_NONBLOCK`: Set the file descriptor in non-blocking mode
378/// `EFD_SEMAPHORE`: Provide semaphore-like semantics for reads
379/// from the new file descriptor.
380pub fn eventfd(initval: u64, flags: EventFlags) -> io::Result<FileDescriptor> {
381	let obj = self::eventfd::EventFd::new(initval, flags);
382
383	let fd = block_on(core_scheduler().insert_object(Arc::new(obj)), None)?;
384
385	Ok(fd)
386}
387
388pub(crate) fn get_object(fd: FileDescriptor) -> io::Result<Arc<dyn ObjectInterface>> {
389	block_on(core_scheduler().get_object(fd), None)
390}
391
392pub(crate) fn insert_object(obj: Arc<dyn ObjectInterface>) -> io::Result<FileDescriptor> {
393	block_on(core_scheduler().insert_object(obj), None)
394}
395
396// The dup system call allocates a new file descriptor that refers
397// to the same open file description as the descriptor oldfd. The new
398// file descriptor number is guaranteed to be the lowest-numbered
399// file descriptor that was unused in the calling process.
400pub(crate) fn dup_object(fd: FileDescriptor) -> io::Result<FileDescriptor> {
401	block_on(core_scheduler().dup_object(fd), None)
402}
403
404pub(crate) fn dup_object2(fd1: FileDescriptor, fd2: FileDescriptor) -> io::Result<FileDescriptor> {
405	block_on(core_scheduler().dup_object2(fd1, fd2), None)
406}
407
408pub(crate) fn remove_object(fd: FileDescriptor) -> io::Result<Arc<dyn ObjectInterface>> {
409	block_on(core_scheduler().remove_object(fd), None)
410}
411
412pub(crate) fn isatty(fd: FileDescriptor) -> io::Result<bool> {
413	let obj = get_object(fd)?;
414	block_on(obj.isatty(), None)
415}