Skip to main content

hermit/fd/
mod.rs

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