hermit/syscalls/
mod.rs

1#![allow(clippy::result_unit_err)]
2
3use alloc::ffi::CString;
4#[cfg(all(target_os = "none", not(feature = "common-os")))]
5use core::alloc::{GlobalAlloc, Layout};
6use core::ffi::{CStr, c_char};
7use core::marker::PhantomData;
8use core::ptr::null;
9
10use dirent_display::Dirent64Display;
11use hermit_sync::Lazy;
12
13pub use self::condvar::*;
14pub use self::entropy::*;
15pub use self::futex::*;
16pub use self::processor::*;
17#[cfg(feature = "newlib")]
18pub use self::recmutex::*;
19pub use self::semaphore::*;
20pub use self::spinlock::*;
21pub use self::system::*;
22pub use self::tasks::*;
23pub use self::timer::*;
24use crate::env;
25use crate::errno::{Errno, ToErrno};
26use crate::executor::block_on;
27use crate::fd::{
28	self, AccessOption, AccessPermission, EventFlags, FileDescriptor, OpenOption, PollFd,
29	dup_object, dup_object2, get_object, isatty, remove_object,
30};
31use crate::fs::{self, FileAttr, SeekWhence};
32#[cfg(all(target_os = "none", not(feature = "common-os")))]
33use crate::mm::ALLOCATOR;
34use crate::syscalls::interfaces::SyscallInterface;
35
36mod condvar;
37mod entropy;
38mod futex;
39pub(crate) mod interfaces;
40#[cfg(feature = "mman")]
41mod mman;
42mod processor;
43#[cfg(feature = "newlib")]
44mod recmutex;
45mod semaphore;
46#[cfg(any(feature = "net", feature = "vsock"))]
47pub mod socket;
48mod spinlock;
49mod system;
50#[cfg(feature = "common-os")]
51pub(crate) mod table;
52mod tasks;
53mod timer;
54
55pub(crate) static SYS: Lazy<&'static dyn SyscallInterface> = Lazy::new(|| {
56	if env::is_uhyve() {
57		&self::interfaces::Uhyve
58	} else {
59		&self::interfaces::Generic
60	}
61});
62
63#[repr(C)]
64#[derive(Debug, Clone, Copy)]
65/// Describes  a  region  of  memory, beginning at `iov_base` address and with the size of `iov_len` bytes.
66struct iovec {
67	/// Starting address
68	pub iov_base: *mut u8,
69	/// Size of the memory pointed to by iov_base.
70	pub iov_len: usize,
71}
72
73const IOV_MAX: usize = 1024;
74
75pub(crate) fn init() {
76	Lazy::force(&SYS);
77
78	// Perform interface-specific initialization steps.
79	SYS.init();
80
81	init_entropy();
82}
83
84/// Interface to allocate memory from system heap
85///
86/// # Errors
87/// Returning a null pointer indicates that either memory is exhausted or
88/// `size` and `align` do not meet this allocator's size or alignment constraints.
89///
90#[cfg(all(target_os = "none", not(feature = "common-os")))]
91#[hermit_macro::system]
92#[unsafe(no_mangle)]
93pub extern "C" fn sys_alloc(size: usize, align: usize) -> *mut u8 {
94	let layout_res = Layout::from_size_align(size, align);
95	if layout_res.is_err() || size == 0 {
96		warn!("__sys_alloc called with size {size:#x}, align {align:#x} is an invalid layout!");
97		return core::ptr::null_mut();
98	}
99	let layout = layout_res.unwrap();
100	let ptr = unsafe { ALLOCATOR.alloc(layout) };
101
102	trace!("__sys_alloc: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
103
104	ptr
105}
106
107#[cfg(all(target_os = "none", not(feature = "common-os")))]
108#[hermit_macro::system]
109#[unsafe(no_mangle)]
110pub extern "C" fn sys_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
111	let layout_res = Layout::from_size_align(size, align);
112	if layout_res.is_err() || size == 0 {
113		warn!(
114			"__sys_alloc_zeroed called with size {size:#x}, align {align:#x} is an invalid layout!"
115		);
116		return core::ptr::null_mut();
117	}
118	let layout = layout_res.unwrap();
119	let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) };
120
121	trace!("__sys_alloc_zeroed: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
122
123	ptr
124}
125
126#[cfg(all(target_os = "none", not(feature = "common-os")))]
127#[hermit_macro::system]
128#[unsafe(no_mangle)]
129pub extern "C" fn sys_malloc(size: usize, align: usize) -> *mut u8 {
130	let layout_res = Layout::from_size_align(size, align);
131	if layout_res.is_err() || size == 0 {
132		warn!("__sys_malloc called with size {size:#x}, align {align:#x} is an invalid layout!");
133		return core::ptr::null_mut();
134	}
135	let layout = layout_res.unwrap();
136	let ptr = unsafe { ALLOCATOR.alloc(layout) };
137
138	trace!("__sys_malloc: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
139
140	ptr
141}
142
143/// Shrink or grow a block of memory to the given `new_size`. The block is described by the given
144/// ptr pointer and layout. If this returns a non-null pointer, then ownership of the memory block
145/// referenced by ptr has been transferred to this allocator. The memory may or may not have been
146/// deallocated, and should be considered unusable (unless of course it was transferred back to the
147/// caller again via the return value of this method). The new memory block is allocated with
148/// layout, but with the size updated to new_size.
149/// If this method returns null, then ownership of the memory block has not been transferred to this
150/// allocator, and the contents of the memory block are unaltered.
151///
152/// # Safety
153/// This function is unsafe because undefined behavior can result if the caller does not ensure all
154/// of the following:
155/// - `ptr` must be currently allocated via this allocator,
156/// - `size` and `align` must be the same layout that was used to allocate that block of memory.
157/// ToDO: verify if the same values for size and align always lead to the same layout
158///
159/// # Errors
160/// Returns null if the new layout does not meet the size and alignment constraints of the
161/// allocator, or if reallocation otherwise fails.
162#[cfg(all(target_os = "none", not(feature = "common-os")))]
163#[hermit_macro::system]
164#[unsafe(no_mangle)]
165pub unsafe extern "C" fn sys_realloc(
166	ptr: *mut u8,
167	size: usize,
168	align: usize,
169	new_size: usize,
170) -> *mut u8 {
171	unsafe {
172		let layout_res = Layout::from_size_align(size, align);
173		if layout_res.is_err() || size == 0 || new_size == 0 {
174			warn!(
175				"__sys_realloc called with ptr {ptr:p}, size {size:#x}, align {align:#x}, new_size {new_size:#x} is an invalid layout!"
176			);
177			return core::ptr::null_mut();
178		}
179		let layout = layout_res.unwrap();
180		let new_ptr = ALLOCATOR.realloc(ptr, layout, new_size);
181
182		if new_ptr.is_null() {
183			debug!(
184				"__sys_realloc failed to resize ptr {ptr:p} with size {size:#x}, align {align:#x}, new_size {new_size:#x} !"
185			);
186		} else {
187			trace!("__sys_realloc: resized memory at {ptr:p}, new address {new_ptr:p}");
188		}
189		new_ptr
190	}
191}
192
193/// Interface to deallocate a memory region from the system heap
194///
195/// # Safety
196/// This function is unsafe because undefined behavior can result if the caller does not ensure all of the following:
197/// - ptr must denote a block of memory currently allocated via this allocator,
198/// - `size` and `align` must be the same values that were used to allocate that block of memory
199/// ToDO: verify if the same values for size and align always lead to the same layout
200///
201/// # Errors
202/// May panic if debug assertions are enabled and invalid parameters `size` or `align` where passed.
203#[cfg(all(target_os = "none", not(feature = "common-os")))]
204#[hermit_macro::system]
205#[unsafe(no_mangle)]
206pub unsafe extern "C" fn sys_dealloc(ptr: *mut u8, size: usize, align: usize) {
207	unsafe {
208		let layout_res = Layout::from_size_align(size, align);
209		if layout_res.is_err() || size == 0 {
210			warn!(
211				"__sys_dealloc called with size {size:#x}, align {align:#x} is an invalid layout!"
212			);
213			debug_assert!(layout_res.is_err(), "__sys_dealloc error: Invalid layout");
214			debug_assert_ne!(size, 0, "__sys_dealloc error: size cannot be 0");
215		} else {
216			trace!("sys_free: deallocate memory at {ptr:p} (size {size:#x})");
217		}
218		let layout = layout_res.unwrap();
219		ALLOCATOR.dealloc(ptr, layout);
220	}
221}
222
223#[cfg(all(target_os = "none", not(feature = "common-os")))]
224#[hermit_macro::system]
225#[unsafe(no_mangle)]
226pub unsafe extern "C" fn sys_free(ptr: *mut u8, size: usize, align: usize) {
227	unsafe {
228		let layout_res = Layout::from_size_align(size, align);
229		if layout_res.is_err() || size == 0 {
230			warn!("__sys_free called with size {size:#x}, align {align:#x} is an invalid layout!");
231			debug_assert!(layout_res.is_err(), "__sys_free error: Invalid layout");
232			debug_assert_ne!(size, 0, "__sys_free error: size cannot be 0");
233		} else {
234			trace!("sys_free: deallocate memory at {ptr:p} (size {size:#x})");
235		}
236		let layout = layout_res.unwrap();
237		ALLOCATOR.dealloc(ptr, layout);
238	}
239}
240
241pub(crate) fn get_application_parameters() -> (i32, *const *const u8, *const *const u8) {
242	SYS.get_application_parameters()
243}
244
245pub(crate) fn shutdown(arg: i32) -> ! {
246	// print some performance statistics
247	crate::arch::kernel::print_statistics();
248
249	SYS.shutdown(arg)
250}
251
252#[hermit_macro::system(errno)]
253#[unsafe(no_mangle)]
254pub unsafe extern "C" fn sys_unlink(name: *const c_char) -> i32 {
255	let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
256
257	fs::unlink(name).map_or_else(|e| -i32::from(e), |()| 0)
258}
259
260#[hermit_macro::system(errno)]
261#[unsafe(no_mangle)]
262pub unsafe extern "C" fn sys_mkdir(name: *const c_char, mode: u32) -> i32 {
263	let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
264	let Some(mode) = AccessPermission::from_bits(mode) else {
265		return -i32::from(Errno::Inval);
266	};
267
268	crate::fs::create_dir(name, mode).map_or_else(|e| -i32::from(e), |()| 0)
269}
270
271#[hermit_macro::system(errno)]
272#[unsafe(no_mangle)]
273pub unsafe extern "C" fn sys_rmdir(name: *const c_char) -> i32 {
274	let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
275
276	crate::fs::remove_dir(name).map_or_else(|e| -i32::from(e), |()| 0)
277}
278
279#[hermit_macro::system(errno)]
280#[unsafe(no_mangle)]
281pub unsafe extern "C" fn sys_stat(name: *const c_char, stat: *mut FileAttr) -> i32 {
282	let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
283
284	match fs::read_stat(name) {
285		Ok(attr) => unsafe {
286			*stat = attr;
287			0
288		},
289		Err(e) => -i32::from(e),
290	}
291}
292
293#[hermit_macro::system(errno)]
294#[unsafe(no_mangle)]
295pub unsafe extern "C" fn sys_lstat(name: *const c_char, stat: *mut FileAttr) -> i32 {
296	let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
297
298	match fs::read_lstat(name) {
299		Ok(attr) => unsafe {
300			*stat = attr;
301			0
302		},
303		Err(e) => -i32::from(e),
304	}
305}
306
307#[hermit_macro::system(errno)]
308#[unsafe(no_mangle)]
309pub unsafe extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 {
310	if stat.is_null() {
311		return -i32::from(Errno::Inval);
312	}
313
314	crate::fd::fstat(fd).map_or_else(
315		|e| -i32::from(e),
316		|v| unsafe {
317			*stat = v;
318			0
319		},
320	)
321}
322
323#[hermit_macro::system(errno)]
324#[unsafe(no_mangle)]
325pub unsafe extern "C" fn sys_opendir(name: *const c_char) -> FileDescriptor {
326	if let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() {
327		crate::fs::opendir(name).unwrap_or_else(|e| -i32::from(e))
328	} else {
329		-i32::from(Errno::Inval)
330	}
331}
332
333#[hermit_macro::system(errno)]
334#[unsafe(no_mangle)]
335pub unsafe extern "C" fn sys_open(name: *const c_char, flags: i32, mode: u32) -> FileDescriptor {
336	let Some(flags) = OpenOption::from_bits(flags) else {
337		return -i32::from(Errno::Inval);
338	};
339	let Some(mode) = AccessPermission::from_bits(mode) else {
340		return -i32::from(Errno::Inval);
341	};
342
343	if let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() {
344		crate::fs::open(name, flags, mode).unwrap_or_else(|e| -i32::from(e))
345	} else {
346		-i32::from(Errno::Inval)
347	}
348}
349
350#[hermit_macro::system]
351#[unsafe(no_mangle)]
352pub unsafe extern "C" fn sys_getcwd(buf: *mut c_char, size: usize) -> *const c_char {
353	let error = |e: Errno| {
354		e.set_errno();
355		null::<c_char>()
356	};
357
358	if size == 0 {
359		return error(Errno::Inval);
360	}
361
362	if buf.is_null() {
363		// Behavior unspecified
364		return error(Errno::Noent);
365	}
366
367	let cwd = match fs::get_cwd() {
368		Err(e) => {
369			return error(e);
370		}
371		Ok(cwd) => cwd,
372	};
373
374	let Ok(cwd) = CString::new(cwd) else {
375		return error(Errno::Noent);
376	};
377
378	if (cwd.count_bytes() + 1) > size {
379		return error(Errno::Range);
380	}
381
382	unsafe {
383		buf.copy_from(cwd.as_ptr(), size);
384	}
385
386	buf
387}
388
389#[hermit_macro::system(errno)]
390#[unsafe(no_mangle)]
391pub extern "C" fn sys_fchdir(_fd: FileDescriptor) -> i32 {
392	-i32::from(Errno::Nosys)
393}
394
395#[hermit_macro::system(errno)]
396#[unsafe(no_mangle)]
397pub unsafe extern "C" fn sys_chdir(path: *mut c_char) -> i32 {
398	if let Ok(name) = unsafe { CStr::from_ptr(path) }.to_str() {
399		crate::fs::set_cwd(name)
400			.map(|()| 0)
401			.unwrap_or_else(|e| -i32::from(e))
402	} else {
403		-i32::from(Errno::Inval)
404	}
405}
406
407#[hermit_macro::system]
408#[unsafe(no_mangle)]
409pub unsafe extern "C" fn sys_umask(umask: u32) -> u32 {
410	crate::fs::umask(AccessPermission::from_bits_truncate(umask)).bits()
411}
412
413#[hermit_macro::system(errno)]
414#[unsafe(no_mangle)]
415pub unsafe extern "C" fn sys_faccessat(
416	dirfd: FileDescriptor,
417	name: *const c_char,
418	_mode: i32,
419	flags: i32,
420) -> i32 {
421	let Some(access_option) = AccessOption::from_bits(flags) else {
422		return -i32::from(Errno::Inval);
423	};
424
425	let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
426		return -i32::from(Errno::Inval);
427	};
428
429	const AT_SYMLINK_NOFOLLOW: i32 = 0x100;
430	const AT_FDCWD: i32 = -100;
431
432	let stat = if name.starts_with("/") || dirfd == AT_FDCWD {
433		let no_follow: bool = (flags & AT_SYMLINK_NOFOLLOW) != 0;
434
435		if no_follow {
436			fs::read_stat(name)
437		} else {
438			fs::read_lstat(name)
439		}
440	} else {
441		warn!("faccessat with directory relative to fd is not implemented!");
442		return -i32::from(Errno::Nosys);
443	};
444
445	match stat {
446		Err(e) => -i32::from(e),
447		Ok(stat) if access_option.can_access(stat.st_mode) => 0,
448		Ok(_) => -i32::from(Errno::Acces),
449	}
450}
451
452#[hermit_macro::system(errno)]
453#[unsafe(no_mangle)]
454pub unsafe extern "C" fn sys_access(name: *const c_char, flags: i32) -> i32 {
455	let Some(access_option) = AccessOption::from_bits(flags) else {
456		return -i32::from(Errno::Inval);
457	};
458
459	if access_option.contains(AccessOption::F_OK) && access_option != AccessOption::F_OK {
460		return -i32::from(Errno::Inval);
461	}
462
463	let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
464		return -i32::from(Errno::Inval);
465	};
466
467	match crate::fs::read_lstat(name) {
468		Err(e) => -i32::from(e),
469		Ok(stat) if access_option.can_access(stat.st_mode) => 0,
470		Ok(_) => -i32::from(Errno::Acces),
471	}
472}
473
474#[hermit_macro::system(errno)]
475#[unsafe(no_mangle)]
476pub unsafe extern "C" fn sys_fchmod(fd: FileDescriptor, mode: u32) -> i32 {
477	let Some(access_permission) = AccessPermission::from_bits(mode) else {
478		return -i32::from(Errno::Inval);
479	};
480
481	crate::fd::chmod(fd, access_permission)
482		.map(|()| 0)
483		.unwrap_or_else(|e| -i32::from(e))
484}
485
486#[hermit_macro::system(errno)]
487#[unsafe(no_mangle)]
488pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 {
489	let obj = remove_object(fd);
490	obj.map_or_else(|e| -i32::from(e), |_| 0)
491}
492
493#[hermit_macro::system(errno)]
494#[unsafe(no_mangle)]
495pub unsafe extern "C" fn sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize {
496	let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast(), len) };
497	crate::fd::read(fd, slice).map_or_else(
498		|e| isize::try_from(-i32::from(e)).unwrap(),
499		|v| v.try_into().unwrap(),
500	)
501}
502
503/// `read()` attempts to read `nbyte` of data to the object referenced by the
504/// descriptor `fd` from a buffer. `read()` performs the same
505/// action, but scatters the input data from the `iovcnt` buffers specified by the
506/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
507///
508/// ```
509/// struct iovec {
510///     char   *iov_base;  /* Base address. */
511///     size_t iov_len;    /* Length. */
512/// };
513/// ```
514///
515/// Each `iovec` entry specifies the base address and length of an area in memory from
516/// which data should be written.  `readv()` will always fill an completely
517/// before proceeding to the next.
518#[hermit_macro::system(errno)]
519#[unsafe(no_mangle)]
520pub unsafe extern "C" fn sys_readv(fd: i32, iov: *const iovec, iovcnt: usize) -> isize {
521	if !(0..=IOV_MAX).contains(&iovcnt) {
522		return (-i32::from(Errno::Inval)).try_into().unwrap();
523	}
524
525	let mut read_bytes: isize = 0;
526	let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
527
528	for iovec_buf in iovec_buffers {
529		let buf = unsafe {
530			core::slice::from_raw_parts_mut(iovec_buf.iov_base.cast(), iovec_buf.iov_len)
531		};
532
533		let len = crate::fd::read(fd, buf).map_or_else(
534			|e| isize::try_from(-i32::from(e)).unwrap(),
535			|v| v.try_into().unwrap(),
536		);
537
538		if len < 0 {
539			return len;
540		}
541
542		read_bytes += len;
543
544		if len < isize::try_from(iovec_buf.iov_len).unwrap() {
545			return read_bytes;
546		}
547	}
548
549	read_bytes
550}
551
552unsafe fn write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
553	let slice = unsafe { core::slice::from_raw_parts(buf, len) };
554	crate::fd::write(fd, slice).map_or_else(
555		|e| isize::try_from(-i32::from(e)).unwrap(),
556		|v| v.try_into().unwrap(),
557	)
558}
559
560#[hermit_macro::system(errno)]
561#[unsafe(no_mangle)]
562pub unsafe extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
563	unsafe { write(fd, buf, len) }
564}
565
566#[hermit_macro::system(errno)]
567#[unsafe(no_mangle)]
568pub unsafe extern "C" fn sys_ftruncate(fd: FileDescriptor, size: usize) -> i32 {
569	fd::truncate(fd, size).map_or_else(|e| -i32::from(e), |()| 0)
570}
571
572#[hermit_macro::system(errno)]
573#[unsafe(no_mangle)]
574pub unsafe extern "C" fn sys_truncate(path: *const c_char, size: usize) -> i32 {
575	let Ok(path) = unsafe { CStr::from_ptr(path) }.to_str() else {
576		return -i32::from(Errno::Inval);
577	};
578
579	fs::truncate(path, size).map_or_else(|e| -i32::from(e), |()| 0)
580}
581
582/// `write()` attempts to write `nbyte` of data to the object referenced by the
583/// descriptor `fd` from a buffer. `writev()` performs the same
584/// action, but gathers the output data from the `iovcnt` buffers specified by the
585/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
586///
587/// ```
588/// struct iovec {
589///     char   *iov_base;  /* Base address. */
590///     size_t iov_len;    /* Length. */
591/// };
592/// ```
593///
594/// Each `iovec` entry specifies the base address and length of an area in memory from
595/// which data should be written.  `writev()` will always write a
596/// complete area before proceeding to the next.
597#[hermit_macro::system(errno)]
598#[unsafe(no_mangle)]
599pub unsafe extern "C" fn sys_writev(fd: FileDescriptor, iov: *const iovec, iovcnt: usize) -> isize {
600	if !(0..=IOV_MAX).contains(&iovcnt) {
601		return (-i32::from(Errno::Inval)).try_into().unwrap();
602	}
603
604	let mut written_bytes: isize = 0;
605	let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
606
607	for iovec_buf in iovec_buffers {
608		let buf = unsafe { core::slice::from_raw_parts(iovec_buf.iov_base, iovec_buf.iov_len) };
609
610		let len = crate::fd::write(fd, buf).map_or_else(
611			|e| isize::try_from(-i32::from(e)).unwrap(),
612			|v| v.try_into().unwrap(),
613		);
614
615		if len < 0 {
616			return len;
617		}
618
619		written_bytes += len;
620
621		if len < isize::try_from(iovec_buf.iov_len).unwrap() {
622			return written_bytes;
623		}
624	}
625
626	written_bytes
627}
628
629#[hermit_macro::system(errno)]
630#[unsafe(no_mangle)]
631pub unsafe extern "C" fn sys_ioctl(
632	fd: FileDescriptor,
633	cmd: i32,
634	argp: *mut core::ffi::c_void,
635) -> i32 {
636	const FIONBIO: i32 = 0x8008_667eu32 as i32;
637
638	if cmd == FIONBIO {
639		let value = unsafe { *(argp as *const i32) };
640		let status_flags = if value != 0 {
641			fd::StatusFlags::O_NONBLOCK
642		} else {
643			fd::StatusFlags::empty()
644		};
645
646		let obj = get_object(fd);
647		obj.map_or_else(
648			|e| -i32::from(e),
649			|v| {
650				block_on(
651					async { v.write().await.set_status_flags(status_flags).await },
652					None,
653				)
654				.map_or_else(|e| -i32::from(e), |()| 0)
655			},
656		)
657	} else {
658		-i32::from(Errno::Inval)
659	}
660}
661
662/// manipulate file descriptor
663#[hermit_macro::system(errno)]
664#[unsafe(no_mangle)]
665pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 {
666	const F_SETFD: i32 = 2;
667	const F_GETFL: i32 = 3;
668	const F_SETFL: i32 = 4;
669	const FD_CLOEXEC: i32 = 1;
670
671	if cmd == F_SETFD && arg == FD_CLOEXEC {
672		0
673	} else if cmd == F_GETFL {
674		let obj = get_object(fd);
675		obj.map_or_else(
676			|e| -i32::from(e),
677			|v| {
678				block_on(async { v.read().await.status_flags().await }, None)
679					.map_or_else(|e| -i32::from(e), |status_flags| status_flags.bits())
680			},
681		)
682	} else if cmd == F_SETFL {
683		let obj = get_object(fd);
684		obj.map_or_else(
685			|e| -i32::from(e),
686			|v| {
687				block_on(
688					async {
689						v.write()
690							.await
691							.set_status_flags(fd::StatusFlags::from_bits_retain(arg))
692							.await
693					},
694					None,
695				)
696				.map_or_else(|e| -i32::from(e), |()| 0)
697			},
698		)
699	} else {
700		-i32::from(Errno::Inval)
701	}
702}
703
704#[hermit_macro::system(errno)]
705#[unsafe(no_mangle)]
706pub extern "C" fn sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> isize {
707	let whence = u8::try_from(whence).unwrap();
708	let whence = SeekWhence::try_from(whence).unwrap();
709	crate::fd::lseek(fd, offset, whence).unwrap_or_else(|e| isize::try_from(-i32::from(e)).unwrap())
710}
711
712#[repr(C)]
713pub struct Dirent64 {
714	/// 64-bit inode number
715	pub d_ino: u64,
716	/// Field without meaning. Kept for BW compatibility.
717	pub d_off: i64,
718	/// Size of this dirent
719	pub d_reclen: u16,
720	/// File type
721	pub d_type: fs::FileType,
722	/// Filename (null-terminated)
723	pub d_name: PhantomData<c_char>,
724}
725impl Dirent64 {
726	/// Creates a [`Dirent64Display`] struct for debug printing.
727	///
728	/// # Safety
729	/// The bytes following the `d_name` must form a valid zero terminated `CStr`. Else we have an
730	/// out-of-bounds read.
731	#[allow(dead_code)]
732	unsafe fn display<'a>(&'a self) -> Dirent64Display<'a> {
733		unsafe { Dirent64Display::new(self) }
734	}
735}
736
737mod dirent_display {
738	use core::ffi::{CStr, c_char};
739	use core::fmt;
740
741	use super::Dirent64;
742
743	/// Helperstruct for unsafe formatting of [`Dirent64`]
744	pub(super) struct Dirent64Display<'a> {
745		dirent: &'a Dirent64,
746	}
747	impl<'a> Dirent64Display<'a> {
748		/// # Safety
749		/// The `d_name` ptr of `dirent` must be valid and zero-terminated.
750		pub(super) unsafe fn new(dirent: &'a Dirent64) -> Self {
751			Self { dirent }
752		}
753	}
754
755	impl<'a> fmt::Debug for Dirent64Display<'a> {
756		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757			let cstr = unsafe { CStr::from_ptr((&raw const self.dirent.d_name).cast::<c_char>()) };
758
759			f.debug_struct("Dirent64")
760				.field("d_ino", &self.dirent.d_ino)
761				.field("d_off", &self.dirent.d_off)
762				.field("d_reclen", &self.dirent.d_reclen)
763				.field("d_type", &self.dirent.d_type)
764				.field("d_name", &cstr)
765				.finish()
766		}
767	}
768}
769
770/// Read the entries of a directory.
771/// Similar as the Linux system-call, this reads up to `count` bytes and returns the number of
772/// bytes written. If the size was not sufficient to list all directory entries, subsequent calls
773/// to this fn return the next entries.
774///
775/// Parameters:
776///
777/// - `fd`: File Descriptor of the directory in question.
778/// -`dirp`: Memory for the kernel to store the filled `Dirent64` objects including the c-strings with the filenames to.
779/// - `count`: Size of the memory region described by `dirp` in bytes.
780///
781/// Return:
782///
783/// The number of bytes read into `dirp` on success. Zero indicates that no more entries remain and
784/// the directories readposition needs to be reset using `sys_lseek`.
785/// Negative numbers encode errors.
786#[hermit_macro::system(errno)]
787#[unsafe(no_mangle)]
788pub unsafe extern "C" fn sys_getdents64(
789	fd: FileDescriptor,
790	dirp: *mut Dirent64,
791	count: usize,
792) -> i64 {
793	debug!("getdents for fd {fd:?} - count: {count}");
794	if dirp.is_null() || count == 0 {
795		return (-i32::from(Errno::Inval)).into();
796	}
797
798	let slice = unsafe { core::slice::from_raw_parts_mut(dirp.cast(), count) };
799
800	let obj = get_object(fd);
801	obj.map_or_else(
802		|_| (-i32::from(Errno::Inval)).into(),
803		|v| {
804			block_on(async { v.read().await.getdents(slice).await }, None)
805				.map_or_else(|e| (-i32::from(e)).into(), |cnt| cnt as i64)
806		},
807	)
808}
809
810#[hermit_macro::system(errno)]
811#[unsafe(no_mangle)]
812pub extern "C" fn sys_dup(fd: i32) -> i32 {
813	dup_object(fd).unwrap_or_else(|e| -i32::from(e))
814}
815
816#[hermit_macro::system(errno)]
817#[unsafe(no_mangle)]
818pub extern "C" fn sys_dup2(fd1: i32, fd2: i32) -> i32 {
819	dup_object2(fd1, fd2).unwrap_or_else(|e| -i32::from(e))
820}
821
822#[hermit_macro::system(errno)]
823#[unsafe(no_mangle)]
824pub extern "C" fn sys_isatty(fd: i32) -> i32 {
825	match isatty(fd) {
826		Err(e) => -i32::from(e),
827		Ok(v) => {
828			if v {
829				1
830			} else {
831				0
832			}
833		}
834	}
835}
836
837#[hermit_macro::system(errno)]
838#[unsafe(no_mangle)]
839pub unsafe extern "C" fn sys_poll(fds: *mut PollFd, nfds: usize, timeout: i32) -> i32 {
840	let slice = unsafe { core::slice::from_raw_parts_mut(fds, nfds) };
841	let timeout = if timeout >= 0 {
842		Some(core::time::Duration::from_millis(
843			timeout.try_into().unwrap(),
844		))
845	} else {
846		None
847	};
848
849	crate::fd::poll(slice, timeout).map_or_else(
850		|e| {
851			if e == Errno::Time { 0 } else { -i32::from(e) }
852		},
853		|v| v.try_into().unwrap(),
854	)
855}
856
857#[hermit_macro::system(errno)]
858#[unsafe(no_mangle)]
859pub extern "C" fn sys_eventfd(initval: u64, flags: i16) -> i32 {
860	if let Some(flags) = EventFlags::from_bits(flags) {
861		crate::fd::eventfd(initval, flags).unwrap_or_else(|e| -i32::from(e))
862	} else {
863		-i32::from(Errno::Inval)
864	}
865}
866
867#[hermit_macro::system]
868#[unsafe(no_mangle)]
869pub extern "C" fn sys_image_start_addr() -> usize {
870	crate::mm::kernel_start_address().as_usize()
871}
872
873#[cfg(test)]
874mod tests {
875	use core::ptr;
876
877	use super::*;
878
879	#[cfg(target_os = "none")]
880	#[test_case]
881	fn test_get_application_parameters() {
882		crate::env::init();
883		let (argc, argv, _envp) = get_application_parameters();
884		assert_ne!(argc, 0);
885		assert_ne!(argv, ptr::null());
886	}
887}