Skip to main content

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, slice};
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, OpenOption, PollFd, RawFd, dup_object,
29	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")]
41pub mod mman;
42mod processor;
43#[cfg(feature = "newlib")]
44mod recmutex;
45mod semaphore;
46#[cfg(any(feature = "net", feature = "virtio-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 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 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 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 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: RawFd, 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) -> RawFd {
326	let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
327		return -i32::from(Errno::Inval);
328	};
329
330	crate::fs::opendir(name).unwrap_or_else(|e| -i32::from(e))
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) -> RawFd {
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	let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
344		return -i32::from(Errno::Inval);
345	};
346
347	crate::fs::open(name, flags, mode).unwrap_or_else(|e| -i32::from(e))
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		ptr::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: RawFd) -> 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	let Ok(name) = unsafe { CStr::from_ptr(path) }.to_str() else {
399		return -i32::from(Errno::Inval);
400	};
401
402	crate::fs::set_cwd(name)
403		.map(|()| 0)
404		.unwrap_or_else(|e| -i32::from(e))
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: RawFd,
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: RawFd, 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: RawFd) -> 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: RawFd, buf: *mut u8, len: usize) -> isize {
496	let slice = unsafe { 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 { slice::from_raw_parts(iov, iovcnt) };
527
528	for iovec_buf in iovec_buffers {
529		let buf =
530			unsafe { slice::from_raw_parts_mut(iovec_buf.iov_base.cast(), iovec_buf.iov_len) };
531
532		let len = crate::fd::read(fd, buf).map_or_else(
533			|e| isize::try_from(-i32::from(e)).unwrap(),
534			|v| v.try_into().unwrap(),
535		);
536
537		if len < 0 {
538			return len;
539		}
540
541		read_bytes += len;
542
543		if len < isize::try_from(iovec_buf.iov_len).unwrap() {
544			return read_bytes;
545		}
546	}
547
548	read_bytes
549}
550
551unsafe fn write(fd: RawFd, buf: *const u8, len: usize) -> isize {
552	let slice = unsafe { slice::from_raw_parts(buf, len) };
553	crate::fd::write(fd, slice).map_or_else(
554		|e| isize::try_from(-i32::from(e)).unwrap(),
555		|v| v.try_into().unwrap(),
556	)
557}
558
559#[hermit_macro::system(errno)]
560#[unsafe(no_mangle)]
561pub unsafe extern "C" fn sys_write(fd: RawFd, buf: *const u8, len: usize) -> isize {
562	unsafe { write(fd, buf, len) }
563}
564
565#[hermit_macro::system(errno)]
566#[unsafe(no_mangle)]
567pub unsafe extern "C" fn sys_ftruncate(fd: RawFd, size: usize) -> i32 {
568	fd::truncate(fd, size).map_or_else(|e| -i32::from(e), |()| 0)
569}
570
571#[hermit_macro::system(errno)]
572#[unsafe(no_mangle)]
573pub unsafe extern "C" fn sys_truncate(path: *const c_char, size: usize) -> i32 {
574	let Ok(path) = unsafe { CStr::from_ptr(path) }.to_str() else {
575		return -i32::from(Errno::Inval);
576	};
577
578	fs::truncate(path, size).map_or_else(|e| -i32::from(e), |()| 0)
579}
580
581/// `write()` attempts to write `nbyte` of data to the object referenced by the
582/// descriptor `fd` from a buffer. `writev()` performs the same
583/// action, but gathers the output data from the `iovcnt` buffers specified by the
584/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
585///
586/// ```
587/// struct iovec {
588///     char   *iov_base;  /* Base address. */
589///     size_t iov_len;    /* Length. */
590/// };
591/// ```
592///
593/// Each `iovec` entry specifies the base address and length of an area in memory from
594/// which data should be written.  `writev()` will always write a
595/// complete area before proceeding to the next.
596#[hermit_macro::system(errno)]
597#[unsafe(no_mangle)]
598pub unsafe extern "C" fn sys_writev(fd: RawFd, iov: *const iovec, iovcnt: usize) -> isize {
599	if !(0..=IOV_MAX).contains(&iovcnt) {
600		return (-i32::from(Errno::Inval)).try_into().unwrap();
601	}
602
603	let mut written_bytes: isize = 0;
604	let iovec_buffers = unsafe { slice::from_raw_parts(iov, iovcnt) };
605
606	for iovec_buf in iovec_buffers {
607		let buf = unsafe { slice::from_raw_parts(iovec_buf.iov_base, iovec_buf.iov_len) };
608
609		let len = crate::fd::write(fd, buf).map_or_else(
610			|e| isize::try_from(-i32::from(e)).unwrap(),
611			|v| v.try_into().unwrap(),
612		);
613
614		if len < 0 {
615			return len;
616		}
617
618		written_bytes += len;
619
620		if len < isize::try_from(iovec_buf.iov_len).unwrap() {
621			return written_bytes;
622		}
623	}
624
625	written_bytes
626}
627
628#[hermit_macro::system(errno)]
629#[unsafe(no_mangle)]
630pub unsafe extern "C" fn sys_ioctl(fd: RawFd, cmd: i32, argp: *mut core::ffi::c_void) -> i32 {
631	const FIONBIO: i32 = 0x8008_667eu32 as i32;
632
633	if cmd == FIONBIO {
634		let value = unsafe { *(argp as *const i32) };
635		let status_flags = if value != 0 {
636			fd::StatusFlags::O_NONBLOCK
637		} else {
638			fd::StatusFlags::empty()
639		};
640
641		let obj = get_object(fd);
642		obj.map_or_else(
643			|e| -i32::from(e),
644			|v| {
645				block_on(
646					async { v.write().await.set_status_flags(status_flags).await },
647					None,
648				)
649				.map_or_else(|e| -i32::from(e), |()| 0)
650			},
651		)
652	} else {
653		-i32::from(Errno::Inval)
654	}
655}
656
657/// Manipulate file descriptor
658#[hermit_macro::system(errno)]
659#[unsafe(no_mangle)]
660pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 {
661	const F_SETFD: i32 = 2;
662	const F_GETFL: i32 = 3;
663	const F_SETFL: i32 = 4;
664	const FD_CLOEXEC: i32 = 1;
665
666	if cmd == F_SETFD && arg == FD_CLOEXEC {
667		0
668	} else if cmd == F_GETFL {
669		let obj = get_object(fd);
670		obj.map_or_else(
671			|e| -i32::from(e),
672			|v| {
673				block_on(async { v.read().await.status_flags().await }, None)
674					.map_or_else(|e| -i32::from(e), |status_flags| status_flags.bits())
675			},
676		)
677	} else if cmd == F_SETFL {
678		let obj = get_object(fd);
679		obj.map_or_else(
680			|e| -i32::from(e),
681			|v| {
682				block_on(
683					async {
684						v.write()
685							.await
686							.set_status_flags(fd::StatusFlags::from_bits_retain(arg))
687							.await
688					},
689					None,
690				)
691				.map_or_else(|e| -i32::from(e), |()| 0)
692			},
693		)
694	} else {
695		-i32::from(Errno::Inval)
696	}
697}
698
699#[hermit_macro::system(errno)]
700#[unsafe(no_mangle)]
701pub extern "C" fn sys_lseek(fd: RawFd, offset: isize, whence: i32) -> isize {
702	let whence = u8::try_from(whence).unwrap();
703	let whence = SeekWhence::try_from(whence).unwrap();
704	crate::fd::lseek(fd, offset, whence).unwrap_or_else(|e| isize::try_from(-i32::from(e)).unwrap())
705}
706
707#[repr(C)]
708pub struct Dirent64 {
709	/// 64-bit inode number
710	pub d_ino: u64,
711	/// Field without meaning. Kept for BW compatibility.
712	pub d_off: i64,
713	/// Size of this dirent
714	pub d_reclen: u16,
715	/// File type
716	pub d_type: fs::FileType,
717	/// Filename (null-terminated)
718	pub d_name: PhantomData<c_char>,
719}
720impl Dirent64 {
721	/// Creates a [`Dirent64Display`] struct for debug printing.
722	///
723	/// # Safety
724	/// The bytes following the `d_name` must form a valid zero terminated `CStr`. Else we have an
725	/// out-of-bounds read.
726	#[allow(dead_code)]
727	unsafe fn display<'a>(&'a self) -> Dirent64Display<'a> {
728		unsafe { Dirent64Display::new(self) }
729	}
730}
731
732mod dirent_display {
733	use core::ffi::{CStr, c_char};
734	use core::fmt;
735
736	use super::Dirent64;
737
738	/// [`Display`] adapter for [`Dirent64`].
739	///
740	/// [`Display`]: fmt::Display
741	pub(super) struct Dirent64Display<'a> {
742		dirent: &'a Dirent64,
743	}
744
745	impl<'a> Dirent64Display<'a> {
746		/// # Safety
747		/// The `d_name` ptr of `dirent` must be valid and zero-terminated.
748		pub(super) unsafe fn new(dirent: &'a Dirent64) -> Self {
749			Self { dirent }
750		}
751	}
752
753	impl<'a> fmt::Debug for Dirent64Display<'a> {
754		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
755			let cstr = unsafe { CStr::from_ptr((&raw const self.dirent.d_name).cast::<c_char>()) };
756
757			f.debug_struct("Dirent64")
758				.field("d_ino", &self.dirent.d_ino)
759				.field("d_off", &self.dirent.d_off)
760				.field("d_reclen", &self.dirent.d_reclen)
761				.field("d_type", &self.dirent.d_type)
762				.field("d_name", &cstr)
763				.finish()
764		}
765	}
766}
767
768/// Read the entries of a directory.
769/// Similar as the Linux system-call, this reads up to `count` bytes and returns the number of
770/// bytes written. If the size was not sufficient to list all directory entries, subsequent calls
771/// to this fn return the next entries.
772///
773/// Parameters:
774///
775/// - `fd`: File Descriptor of the directory in question.
776/// -`dirp`: Memory for the kernel to store the filled `Dirent64` objects including the c-strings with the filenames to.
777/// - `count`: Size of the memory region described by `dirp` in bytes.
778///
779/// Return:
780///
781/// The number of bytes read into `dirp` on success. Zero indicates that no more entries remain and
782/// the directories readposition needs to be reset using `sys_lseek`.
783/// Negative numbers encode errors.
784#[hermit_macro::system(errno)]
785#[unsafe(no_mangle)]
786pub unsafe extern "C" fn sys_getdents64(fd: RawFd, dirp: *mut Dirent64, count: usize) -> i64 {
787	debug!("getdents for fd {fd:?} - count: {count}");
788	if dirp.is_null() || count == 0 {
789		return (-i32::from(Errno::Inval)).into();
790	}
791
792	let slice = unsafe { slice::from_raw_parts_mut(dirp.cast(), count) };
793
794	let obj = get_object(fd);
795	obj.map_or_else(
796		|_| (-i32::from(Errno::Inval)).into(),
797		|v| {
798			block_on(async { v.read().await.getdents(slice).await }, None)
799				.map_or_else(|e| (-i32::from(e)).into(), |cnt| cnt as i64)
800		},
801	)
802}
803
804#[hermit_macro::system(errno)]
805#[unsafe(no_mangle)]
806pub extern "C" fn sys_dup(fd: i32) -> i32 {
807	dup_object(fd).unwrap_or_else(|e| -i32::from(e))
808}
809
810#[hermit_macro::system(errno)]
811#[unsafe(no_mangle)]
812pub extern "C" fn sys_dup2(fd1: i32, fd2: i32) -> i32 {
813	dup_object2(fd1, fd2).unwrap_or_else(|e| -i32::from(e))
814}
815
816#[hermit_macro::system(errno)]
817#[unsafe(no_mangle)]
818pub extern "C" fn sys_isatty(fd: i32) -> i32 {
819	match isatty(fd) {
820		Err(e) => -i32::from(e),
821		Ok(v) => {
822			if v {
823				1
824			} else {
825				0
826			}
827		}
828	}
829}
830
831#[hermit_macro::system(errno)]
832#[unsafe(no_mangle)]
833pub unsafe extern "C" fn sys_poll(fds: *mut PollFd, nfds: usize, timeout: i32) -> i32 {
834	let slice = unsafe { slice::from_raw_parts_mut(fds, nfds) };
835	let timeout = if timeout >= 0 {
836		Some(core::time::Duration::from_millis(
837			timeout.try_into().unwrap(),
838		))
839	} else {
840		None
841	};
842
843	crate::fd::poll(slice, timeout).map_or_else(
844		|e| {
845			if e == Errno::Time { 0 } else { -i32::from(e) }
846		},
847		|v| v.try_into().unwrap(),
848	)
849}
850
851#[hermit_macro::system(errno)]
852#[unsafe(no_mangle)]
853pub extern "C" fn sys_eventfd(initval: u64, flags: i16) -> i32 {
854	let Some(flags) = EventFlags::from_bits(flags) else {
855		return -i32::from(Errno::Inval);
856	};
857
858	crate::fd::eventfd(initval, flags).unwrap_or_else(|e| -i32::from(e))
859}
860
861#[hermit_macro::system]
862#[unsafe(no_mangle)]
863pub extern "C" fn sys_image_start_addr() -> usize {
864	crate::mm::kernel_start_address().as_usize()
865}
866
867#[cfg(test)]
868mod tests {
869	use super::*;
870
871	#[cfg(target_os = "none")]
872	#[test_case]
873	fn test_get_application_parameters() {
874		crate::env::init();
875		let (argc, argv, _envp) = get_application_parameters();
876		assert_ne!(argc, 0);
877		assert_ne!(argv, ptr::null());
878	}
879}