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