Skip to main content

hermit/fd/
mod.rs

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