hermit/fd/
mod.rs

1use alloc::boxed::Box;
2use alloc::sync::Arc;
3use core::future::{self, Future};
4use core::mem::MaybeUninit;
5use core::task::Poll::{Pending, Ready};
6use core::time::Duration;
7
8use async_trait::async_trait;
9#[cfg(feature = "net")]
10use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
11
12use crate::arch::kernel::core_local::core_scheduler;
13use crate::errno::Errno;
14use crate::executor::block_on;
15use crate::fs::{FileAttr, SeekWhence};
16use crate::io;
17
18mod eventfd;
19#[cfg(any(feature = "net", 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 = "net", feature = "vsock"))]
28#[derive(Debug)]
29pub(crate) enum Endpoint {
30	#[cfg(feature = "net")]
31	Ip(IpEndpoint),
32	#[cfg(feature = "vsock")]
33	Vsock(socket::vsock::VsockEndpoint),
34}
35
36#[cfg(any(feature = "net", feature = "vsock"))]
37#[derive(Debug)]
38pub(crate) enum ListenEndpoint {
39	#[cfg(feature = "net")]
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	/// Options for checking file permissions or existence
74	#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
75	pub struct AccessOption: i32 {
76		/// Test for read permission
77		const R_OK = 4;
78		/// Test for write permission
79		const W_OK = 2;
80		/// Test for execution permission
81		const X_OK = 1;
82		/// Test for existence
83		const F_OK = 0;
84	}
85}
86
87impl AccessOption {
88	/// Verifies if the current access options are all valid for the provided file access permissions
89	pub fn can_access(&self, access_permissions: AccessPermission) -> bool {
90		if self.contains(AccessOption::R_OK)
91			&& !access_permissions.contains(AccessPermission::S_IRUSR)
92			&& !access_permissions.contains(AccessPermission::S_IRGRP)
93			&& !access_permissions.contains(AccessPermission::S_IROTH)
94		{
95			return false;
96		}
97
98		if self.contains(AccessOption::W_OK)
99			&& !access_permissions.contains(AccessPermission::S_IWUSR)
100			&& !access_permissions.contains(AccessPermission::S_IWGRP)
101			&& !access_permissions.contains(AccessPermission::S_IWOTH)
102		{
103			return false;
104		}
105
106		if self.contains(AccessOption::X_OK)
107			&& !access_permissions.contains(AccessPermission::S_IXUSR)
108			&& !access_permissions.contains(AccessPermission::S_IXGRP)
109			&& !access_permissions.contains(AccessPermission::S_IXOTH)
110		{
111			return false;
112		}
113
114		true
115	}
116}
117
118bitflags! {
119	/// File status flags.
120	#[derive(Debug, Copy, Clone, Default)]
121	pub struct StatusFlags: i32 {
122		const O_APPEND = 0o2000;
123		const O_NONBLOCK = 0o4000;
124	}
125}
126
127bitflags! {
128	#[derive(Debug, Copy, Clone, Default)]
129	pub struct PollEvent: i16 {
130		const POLLIN = 0x1;
131		const POLLPRI = 0x2;
132		const POLLOUT = 0x4;
133		const POLLERR = 0x8;
134		const POLLHUP = 0x10;
135		const POLLNVAL = 0x20;
136		const POLLRDNORM = 0x040;
137		const POLLRDBAND = 0x080;
138		const POLLWRNORM = 0x0100;
139		const POLLWRBAND = 0x0200;
140		const POLLRDHUP = 0x2000;
141	}
142}
143
144#[repr(C)]
145#[derive(Debug, Default, Copy, Clone)]
146pub struct PollFd {
147	/// file descriptor
148	pub fd: i32,
149	/// events to look for
150	pub events: PollEvent,
151	/// events returned
152	pub revents: PollEvent,
153}
154
155bitflags! {
156	#[derive(Debug, Default, Copy, Clone)]
157	pub struct EventFlags: i16 {
158		const EFD_SEMAPHORE = 0o1;
159		const EFD_NONBLOCK = 0o4000;
160		const EFD_CLOEXEC = 0o40000;
161	}
162}
163
164bitflags! {
165	#[derive(Debug, Copy, Clone)]
166	pub struct AccessPermission: u32 {
167		const S_IFMT = 0o170_000;
168		const S_IFSOCK = 0o140_000;
169		const S_IFLNK = 0o120_000;
170		const S_IFREG = 0o100_000;
171		const S_IFBLK = 0o060_000;
172		const S_IFDIR = 0o040_000;
173		const S_IFCHR = 0o020_000;
174		const S_IFIFO = 0o010_000;
175		const S_IRUSR = 0o400;
176		const S_IWUSR = 0o200;
177		const S_IXUSR = 0o100;
178		const S_IRWXU = 0o700;
179		const S_IRGRP = 0o040;
180		const S_IWGRP = 0o020;
181		const S_IXGRP = 0o010;
182		const S_IRWXG = 0o070;
183		const S_IROTH = 0o004;
184		const S_IWOTH = 0o002;
185		const S_IXOTH = 0o001;
186		const S_IRWXO = 0o007;
187		// Allow bits unknown to us to be set externally. See bitflags documentation for further explanation.
188		const _ = !0;
189	}
190}
191
192impl Default for AccessPermission {
193	fn default() -> Self {
194		AccessPermission::from_bits(0o666).unwrap()
195	}
196}
197
198#[async_trait]
199pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug {
200	/// check if an IO event is possible
201	async fn poll(&self, _event: PollEvent) -> io::Result<PollEvent> {
202		Ok(PollEvent::empty())
203	}
204
205	/// `async_read` attempts to read `len` bytes from the object references
206	/// by the descriptor
207	async fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
208		Err(Errno::Nosys)
209	}
210
211	/// `async_write` attempts to write `len` bytes to the object references
212	/// by the descriptor
213	async fn write(&self, _buf: &[u8]) -> io::Result<usize> {
214		Err(Errno::Nosys)
215	}
216
217	/// `lseek` function repositions the offset of the file descriptor fildes
218	async fn lseek(&self, _offset: isize, _whence: SeekWhence) -> io::Result<isize> {
219		Err(Errno::Inval)
220	}
221
222	/// `fstat`
223	async fn fstat(&self) -> io::Result<FileAttr> {
224		Err(Errno::Inval)
225	}
226
227	/// `getdents` fills the given buffer `_buf` with [`Dirent64`](crate::syscalls::Dirent64)
228	/// formatted entries of a directory, imitating the Linux `getdents64` syscall.
229	/// On success, the number of bytes read is returned.  On end of directory, 0 is returned.  On error, -1 is returned
230	async fn getdents(&self, _buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
231		Err(Errno::Inval)
232	}
233
234	/// `accept` a connection on a socket
235	#[cfg(any(feature = "net", feature = "vsock"))]
236	async fn accept(
237		&mut self,
238	) -> io::Result<(Arc<async_lock::RwLock<dyn ObjectInterface>>, Endpoint)> {
239		Err(Errno::Inval)
240	}
241
242	/// initiate a connection on a socket
243	#[cfg(any(feature = "net", feature = "vsock"))]
244	async fn connect(&mut self, _endpoint: Endpoint) -> io::Result<()> {
245		Err(Errno::Inval)
246	}
247
248	/// `bind` a name to a socket
249	#[cfg(any(feature = "net", feature = "vsock"))]
250	async fn bind(&mut self, _name: ListenEndpoint) -> io::Result<()> {
251		Err(Errno::Inval)
252	}
253
254	/// `listen` for connections on a socket
255	#[cfg(any(feature = "net", feature = "vsock"))]
256	async fn listen(&mut self, _backlog: i32) -> io::Result<()> {
257		Err(Errno::Inval)
258	}
259
260	/// `setsockopt` sets options on sockets
261	#[cfg(any(feature = "net", feature = "vsock"))]
262	async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()> {
263		Err(Errno::Notsock)
264	}
265
266	/// `getsockopt` gets options on sockets
267	#[cfg(any(feature = "net", feature = "vsock"))]
268	async fn getsockopt(&self, _opt: SocketOption) -> io::Result<bool> {
269		Err(Errno::Notsock)
270	}
271
272	/// `getsockname` gets socket name
273	#[cfg(any(feature = "net", feature = "vsock"))]
274	async fn getsockname(&self) -> io::Result<Option<Endpoint>> {
275		Ok(None)
276	}
277
278	/// `getpeername` get address of connected peer
279	#[cfg(any(feature = "net", feature = "vsock"))]
280	#[allow(dead_code)]
281	async fn getpeername(&self) -> io::Result<Option<Endpoint>> {
282		Ok(None)
283	}
284
285	/// receive a message from a socket
286	#[cfg(any(feature = "net", feature = "vsock"))]
287	async fn recvfrom(&self, _buffer: &mut [MaybeUninit<u8>]) -> io::Result<(usize, Endpoint)> {
288		Err(Errno::Nosys)
289	}
290
291	/// send a message from a socket
292	///
293	/// The sendto() function shall send a message.
294	/// If the socket is a connectionless-mode socket, the message shall
295	/// If a peer address has been prespecified, either the message shall
296	/// be sent to the address specified by dest_addr (overriding the pre-specified peer
297	/// address).
298	#[cfg(any(feature = "net", feature = "vsock"))]
299	async fn sendto(&self, _buffer: &[u8], _endpoint: Endpoint) -> io::Result<usize> {
300		Err(Errno::Nosys)
301	}
302
303	/// shut down part of a full-duplex connection
304	#[cfg(any(feature = "net", feature = "vsock"))]
305	async fn shutdown(&self, _how: i32) -> io::Result<()> {
306		Err(Errno::Nosys)
307	}
308
309	/// Returns the file status flags.
310	async fn status_flags(&self) -> io::Result<StatusFlags> {
311		Err(Errno::Nosys)
312	}
313
314	/// Sets the file status flags.
315	async fn set_status_flags(&mut self, _status_flags: StatusFlags) -> io::Result<()> {
316		Err(Errno::Nosys)
317	}
318
319	/// Truncates the file
320	async fn truncate(&self, _size: usize) -> io::Result<()> {
321		Err(Errno::Nosys)
322	}
323
324	/// Changes access permissions to the file
325	async fn chmod(&self, _access_permission: AccessPermission) -> io::Result<()> {
326		Err(Errno::Nosys)
327	}
328
329	/// `isatty` returns `true` for a terminal device
330	async fn isatty(&self) -> io::Result<bool> {
331		Ok(false)
332	}
333}
334
335pub(crate) fn read(fd: FileDescriptor, buf: &mut [u8]) -> io::Result<usize> {
336	let obj = get_object(fd)?;
337
338	if buf.is_empty() {
339		return Ok(0);
340	}
341
342	block_on(async { obj.read().await.read(buf).await }, None)
343}
344
345pub(crate) fn lseek(fd: FileDescriptor, offset: isize, whence: SeekWhence) -> io::Result<isize> {
346	let obj = get_object(fd)?;
347
348	block_on(async { obj.read().await.lseek(offset, whence).await }, None)
349}
350
351pub(crate) fn chmod(fd: FileDescriptor, mode: AccessPermission) -> io::Result<()> {
352	let obj = get_object(fd)?;
353
354	block_on(async { obj.read().await.chmod(mode).await }, None)
355}
356
357pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> io::Result<usize> {
358	let obj = get_object(fd)?;
359
360	if buf.is_empty() {
361		return Ok(0);
362	}
363
364	block_on(async { obj.read().await.write(buf).await }, None)
365}
366
367pub(crate) fn truncate(fd: FileDescriptor, length: usize) -> io::Result<()> {
368	let obj = get_object(fd)?;
369	block_on(async { obj.read().await.truncate(length).await }, None)
370}
371
372async fn poll_fds(fds: &mut [PollFd]) -> io::Result<u64> {
373	future::poll_fn(|cx| {
374		let mut counter: u64 = 0;
375
376		for i in &mut *fds {
377			let fd = i.fd;
378			i.revents = PollEvent::empty();
379			if let Ok(obj) = core_scheduler().get_object(fd) {
380				let mut pinned = core::pin::pin!(async { obj.read().await.poll(i.events).await });
381				if let Ready(Ok(e)) = pinned.as_mut().poll(cx)
382					&& !e.is_empty()
383				{
384					counter += 1;
385					i.revents = e;
386				}
387			}
388		}
389
390		if counter > 0 {
391			Ready(Ok(counter))
392		} else {
393			Pending
394		}
395	})
396	.await
397}
398
399/// Wait for some event on a file descriptor.
400///
401/// The unix-like `poll` waits for one of a set of file descriptors
402/// to become ready to perform I/O. The set of file descriptors to be
403/// monitored is specified in the `fds` argument, which is an array
404/// of structs of `PollFd`.
405pub fn poll(fds: &mut [PollFd], timeout: Option<Duration>) -> io::Result<u64> {
406	let result = block_on(poll_fds(fds), timeout);
407	if let Err(ref e) = result
408		&& timeout.is_some()
409	{
410		// A return value of zero indicates that the system call timed out
411		if *e == Errno::Again {
412			return Ok(0);
413		}
414	}
415
416	result
417}
418
419pub fn fstat(fd: FileDescriptor) -> io::Result<FileAttr> {
420	let obj = get_object(fd)?;
421	block_on(async { obj.read().await.fstat().await }, None)
422}
423
424/// Wait for some event on a file descriptor.
425///
426/// `eventfd` creates an linux-like "eventfd object" that can be used
427/// as an event wait/notify mechanism by user-space applications, and by
428/// the kernel to notify user-space applications of events. The
429/// object contains an unsigned 64-bit integer counter
430/// that is maintained by the kernel. This counter is initialized
431/// with the value specified in the argument `initval`.
432///
433/// As its return value, `eventfd` returns a new file descriptor that
434/// can be used to refer to the eventfd object.
435///
436/// The following values may be bitwise set in flags to change the
437/// behavior of `eventfd`:
438///
439/// `EFD_NONBLOCK`: Set the file descriptor in non-blocking mode
440/// `EFD_SEMAPHORE`: Provide semaphore-like semantics for reads
441/// from the new file descriptor.
442pub fn eventfd(initval: u64, flags: EventFlags) -> io::Result<FileDescriptor> {
443	let obj = self::eventfd::EventFd::new(initval, flags);
444
445	let fd = core_scheduler().insert_object(Arc::new(async_lock::RwLock::new(obj)))?;
446
447	Ok(fd)
448}
449
450pub(crate) fn get_object(
451	fd: FileDescriptor,
452) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
453	core_scheduler().get_object(fd)
454}
455
456pub(crate) fn insert_object(
457	obj: Arc<async_lock::RwLock<dyn ObjectInterface>>,
458) -> io::Result<FileDescriptor> {
459	core_scheduler().insert_object(obj)
460}
461
462// The dup system call allocates a new file descriptor that refers
463// to the same open file description as the descriptor oldfd. The new
464// file descriptor number is guaranteed to be the lowest-numbered
465// file descriptor that was unused in the calling process.
466pub(crate) fn dup_object(fd: FileDescriptor) -> io::Result<FileDescriptor> {
467	core_scheduler().dup_object(fd)
468}
469
470pub(crate) fn dup_object2(fd1: FileDescriptor, fd2: FileDescriptor) -> io::Result<FileDescriptor> {
471	core_scheduler().dup_object2(fd1, fd2)
472}
473
474pub(crate) fn remove_object(
475	fd: FileDescriptor,
476) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
477	core_scheduler().remove_object(fd)
478}
479
480pub(crate) fn isatty(fd: FileDescriptor) -> io::Result<bool> {
481	let obj = get_object(fd)?;
482	block_on(async { obj.read().await.isatty().await }, None)
483}