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 = "tcp", feature = "udp", 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 unsafe extern "C" fn sys_chdir(path: *mut c_char) -> i32 {
392	if let Ok(name) = unsafe { CStr::from_ptr(path) }.to_str() {
393		crate::fs::set_cwd(name)
394			.map(|()| 0)
395			.unwrap_or_else(|e| -i32::from(e))
396	} else {
397		-i32::from(Errno::Inval)
398	}
399}
400
401#[hermit_macro::system]
402#[unsafe(no_mangle)]
403pub unsafe extern "C" fn sys_umask(umask: u32) -> u32 {
404	crate::fs::umask(AccessPermission::from_bits_truncate(umask)).bits()
405}
406
407#[hermit_macro::system(errno)]
408#[unsafe(no_mangle)]
409pub unsafe extern "C" fn sys_faccessat(
410	dirfd: FileDescriptor,
411	name: *const c_char,
412	_mode: i32,
413	flags: i32,
414) -> i32 {
415	let Some(access_option) = AccessOption::from_bits(flags) else {
416		return -i32::from(Errno::Inval);
417	};
418
419	let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
420		return -i32::from(Errno::Inval);
421	};
422
423	const AT_SYMLINK_NOFOLLOW: i32 = 0x100;
424	const AT_FDCWD: i32 = -100;
425
426	let stat = if name.starts_with("/") || dirfd == AT_FDCWD {
427		let no_follow: bool = (flags & AT_SYMLINK_NOFOLLOW) != 0;
428
429		if no_follow {
430			fs::read_stat(name)
431		} else {
432			fs::read_lstat(name)
433		}
434	} else {
435		warn!("faccessat with directory relative to fd is not implemented!");
436		return -i32::from(Errno::Nosys);
437	};
438
439	match stat {
440		Err(e) => -i32::from(e),
441		Ok(stat) if access_option.can_access(stat.st_mode) => 0,
442		Ok(_) => -i32::from(Errno::Acces),
443	}
444}
445
446#[hermit_macro::system(errno)]
447#[unsafe(no_mangle)]
448pub unsafe extern "C" fn sys_access(name: *const c_char, flags: i32) -> i32 {
449	let Some(access_option) = AccessOption::from_bits(flags) else {
450		return -i32::from(Errno::Inval);
451	};
452
453	if access_option.contains(AccessOption::F_OK) && access_option != AccessOption::F_OK {
454		return -i32::from(Errno::Inval);
455	}
456
457	let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
458		return -i32::from(Errno::Inval);
459	};
460
461	match crate::fs::read_lstat(name) {
462		Err(e) => -i32::from(e),
463		Ok(stat) if access_option.can_access(stat.st_mode) => 0,
464		Ok(_) => -i32::from(Errno::Acces),
465	}
466}
467
468#[hermit_macro::system(errno)]
469#[unsafe(no_mangle)]
470pub unsafe extern "C" fn sys_fchmod(fd: FileDescriptor, mode: u32) -> i32 {
471	let Some(access_permission) = AccessPermission::from_bits(mode) else {
472		return -i32::from(Errno::Inval);
473	};
474
475	crate::fd::chmod(fd, access_permission)
476		.map(|()| 0)
477		.unwrap_or_else(|e| -i32::from(e))
478}
479
480#[hermit_macro::system(errno)]
481#[unsafe(no_mangle)]
482pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 {
483	let obj = remove_object(fd);
484	obj.map_or_else(|e| -i32::from(e), |_| 0)
485}
486
487#[hermit_macro::system(errno)]
488#[unsafe(no_mangle)]
489pub unsafe extern "C" fn sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize {
490	let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast(), len) };
491	crate::fd::read(fd, slice).map_or_else(
492		|e| isize::try_from(-i32::from(e)).unwrap(),
493		|v| v.try_into().unwrap(),
494	)
495}
496
497/// `read()` attempts to read `nbyte` of data to the object referenced by the
498/// descriptor `fd` from a buffer. `read()` performs the same
499/// action, but scatters the input data from the `iovcnt` buffers specified by the
500/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
501///
502/// ```
503/// struct iovec {
504///     char   *iov_base;  /* Base address. */
505///     size_t iov_len;    /* Length. */
506/// };
507/// ```
508///
509/// Each `iovec` entry specifies the base address and length of an area in memory from
510/// which data should be written.  `readv()` will always fill an completely
511/// before proceeding to the next.
512#[hermit_macro::system(errno)]
513#[unsafe(no_mangle)]
514pub unsafe extern "C" fn sys_readv(fd: i32, iov: *const iovec, iovcnt: usize) -> isize {
515	if !(0..=IOV_MAX).contains(&iovcnt) {
516		return (-i32::from(Errno::Inval)).try_into().unwrap();
517	}
518
519	let mut read_bytes: isize = 0;
520	let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
521
522	for iovec_buf in iovec_buffers {
523		let buf = unsafe {
524			core::slice::from_raw_parts_mut(iovec_buf.iov_base.cast(), iovec_buf.iov_len)
525		};
526
527		let len = crate::fd::read(fd, buf).map_or_else(
528			|e| isize::try_from(-i32::from(e)).unwrap(),
529			|v| v.try_into().unwrap(),
530		);
531
532		if len < 0 {
533			return len;
534		}
535
536		read_bytes += len;
537
538		if len < isize::try_from(iovec_buf.iov_len).unwrap() {
539			return read_bytes;
540		}
541	}
542
543	read_bytes
544}
545
546unsafe fn write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
547	let slice = unsafe { core::slice::from_raw_parts(buf, len) };
548	crate::fd::write(fd, slice).map_or_else(
549		|e| isize::try_from(-i32::from(e)).unwrap(),
550		|v| v.try_into().unwrap(),
551	)
552}
553
554#[hermit_macro::system(errno)]
555#[unsafe(no_mangle)]
556pub unsafe extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
557	unsafe { write(fd, buf, len) }
558}
559
560#[hermit_macro::system(errno)]
561#[unsafe(no_mangle)]
562pub unsafe extern "C" fn sys_ftruncate(fd: FileDescriptor, size: usize) -> i32 {
563	fd::truncate(fd, size).map_or_else(|e| -i32::from(e), |()| 0)
564}
565
566#[hermit_macro::system(errno)]
567#[unsafe(no_mangle)]
568pub unsafe extern "C" fn sys_truncate(path: *const c_char, size: usize) -> i32 {
569	let Ok(path) = unsafe { CStr::from_ptr(path) }.to_str() else {
570		return -i32::from(Errno::Inval);
571	};
572
573	fs::truncate(path, size).map_or_else(|e| -i32::from(e), |()| 0)
574}
575
576/// `write()` attempts to write `nbyte` of data to the object referenced by the
577/// descriptor `fd` from a buffer. `writev()` performs the same
578/// action, but gathers the output data from the `iovcnt` buffers specified by the
579/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
580///
581/// ```
582/// struct iovec {
583///     char   *iov_base;  /* Base address. */
584///     size_t iov_len;    /* Length. */
585/// };
586/// ```
587///
588/// Each `iovec` entry specifies the base address and length of an area in memory from
589/// which data should be written.  `writev()` will always write a
590/// complete area before proceeding to the next.
591#[hermit_macro::system(errno)]
592#[unsafe(no_mangle)]
593pub unsafe extern "C" fn sys_writev(fd: FileDescriptor, iov: *const iovec, iovcnt: usize) -> isize {
594	if !(0..=IOV_MAX).contains(&iovcnt) {
595		return (-i32::from(Errno::Inval)).try_into().unwrap();
596	}
597
598	let mut written_bytes: isize = 0;
599	let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
600
601	for iovec_buf in iovec_buffers {
602		let buf = unsafe { core::slice::from_raw_parts(iovec_buf.iov_base, iovec_buf.iov_len) };
603
604		let len = crate::fd::write(fd, buf).map_or_else(
605			|e| isize::try_from(-i32::from(e)).unwrap(),
606			|v| v.try_into().unwrap(),
607		);
608
609		if len < 0 {
610			return len;
611		}
612
613		written_bytes += len;
614
615		if len < isize::try_from(iovec_buf.iov_len).unwrap() {
616			return written_bytes;
617		}
618	}
619
620	written_bytes
621}
622
623#[hermit_macro::system(errno)]
624#[unsafe(no_mangle)]
625pub unsafe extern "C" fn sys_ioctl(
626	fd: FileDescriptor,
627	cmd: i32,
628	argp: *mut core::ffi::c_void,
629) -> i32 {
630	const FIONBIO: i32 = 0x8008_667eu32 as i32;
631
632	if cmd == FIONBIO {
633		let value = unsafe { *(argp as *const i32) };
634		let status_flags = if value != 0 {
635			fd::StatusFlags::O_NONBLOCK
636		} else {
637			fd::StatusFlags::empty()
638		};
639
640		let obj = get_object(fd);
641		obj.map_or_else(
642			|e| -i32::from(e),
643			|v| {
644				block_on((*v).set_status_flags(status_flags), None)
645					.map_or_else(|e| -i32::from(e), |()| 0)
646			},
647		)
648	} else {
649		-i32::from(Errno::Inval)
650	}
651}
652
653/// manipulate file descriptor
654#[hermit_macro::system(errno)]
655#[unsafe(no_mangle)]
656pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 {
657	const F_SETFD: i32 = 2;
658	const F_GETFL: i32 = 3;
659	const F_SETFL: i32 = 4;
660	const FD_CLOEXEC: i32 = 1;
661
662	if cmd == F_SETFD && arg == FD_CLOEXEC {
663		0
664	} else if cmd == F_GETFL {
665		let obj = get_object(fd);
666		obj.map_or_else(
667			|e| -i32::from(e),
668			|v| {
669				block_on((*v).status_flags(), None)
670					.map_or_else(|e| -i32::from(e), |status_flags| status_flags.bits())
671			},
672		)
673	} else if cmd == F_SETFL {
674		let obj = get_object(fd);
675		obj.map_or_else(
676			|e| -i32::from(e),
677			|v| {
678				block_on(
679					(*v).set_status_flags(fd::StatusFlags::from_bits_retain(arg)),
680					None,
681				)
682				.map_or_else(|e| -i32::from(e), |()| 0)
683			},
684		)
685	} else {
686		-i32::from(Errno::Inval)
687	}
688}
689
690#[hermit_macro::system(errno)]
691#[unsafe(no_mangle)]
692pub extern "C" fn sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> isize {
693	let whence = u8::try_from(whence).unwrap();
694	let whence = SeekWhence::try_from(whence).unwrap();
695	crate::fd::lseek(fd, offset, whence).unwrap_or_else(|e| isize::try_from(-i32::from(e)).unwrap())
696}
697
698#[repr(C)]
699pub struct Dirent64 {
700	/// 64-bit inode number
701	pub d_ino: u64,
702	/// Field without meaning. Kept for BW compatibility.
703	pub d_off: i64,
704	/// Size of this dirent
705	pub d_reclen: u16,
706	/// File type
707	pub d_type: fs::FileType,
708	/// Filename (null-terminated)
709	pub d_name: PhantomData<c_char>,
710}
711impl Dirent64 {
712	/// Creates a [`Dirent64Display`] struct for debug printing.
713	///
714	/// # Safety
715	/// The bytes following the `d_name` must form a valid zero terminated `CStr`. Else we have an
716	/// out-of-bounds read.
717	#[allow(dead_code)]
718	unsafe fn display<'a>(&'a self) -> Dirent64Display<'a> {
719		unsafe { Dirent64Display::new(self) }
720	}
721}
722
723mod dirent_display {
724	use core::ffi::{CStr, c_char};
725	use core::fmt;
726
727	use super::Dirent64;
728
729	/// Helperstruct for unsafe formatting of [`Dirent64`]
730	pub(super) struct Dirent64Display<'a> {
731		dirent: &'a Dirent64,
732	}
733	impl<'a> Dirent64Display<'a> {
734		/// # Safety
735		/// The `d_name` ptr of `dirent` must be valid and zero-terminated.
736		pub(super) unsafe fn new(dirent: &'a Dirent64) -> Self {
737			Self { dirent }
738		}
739	}
740
741	impl<'a> fmt::Debug for Dirent64Display<'a> {
742		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
743			let cstr = unsafe { CStr::from_ptr((&raw const self.dirent.d_name).cast::<c_char>()) };
744
745			f.debug_struct("Dirent64")
746				.field("d_ino", &self.dirent.d_ino)
747				.field("d_off", &self.dirent.d_off)
748				.field("d_reclen", &self.dirent.d_reclen)
749				.field("d_type", &self.dirent.d_type)
750				.field("d_name", &cstr)
751				.finish()
752		}
753	}
754}
755
756/// Read the entries of a directory.
757/// Similar as the Linux system-call, this reads up to `count` bytes and returns the number of
758/// bytes written. If the size was not sufficient to list all directory entries, subsequent calls
759/// to this fn return the next entries.
760///
761/// Parameters:
762///
763/// - `fd`: File Descriptor of the directory in question.
764/// -`dirp`: Memory for the kernel to store the filled `Dirent64` objects including the c-strings with the filenames to.
765/// - `count`: Size of the memory region described by `dirp` in bytes.
766///
767/// Return:
768///
769/// The number of bytes read into `dirp` on success. Zero indicates that no more entries remain and
770/// the directories readposition needs to be reset using `sys_lseek`.
771/// Negative numbers encode errors.
772#[hermit_macro::system(errno)]
773#[unsafe(no_mangle)]
774pub unsafe extern "C" fn sys_getdents64(
775	fd: FileDescriptor,
776	dirp: *mut Dirent64,
777	count: usize,
778) -> i64 {
779	debug!("getdents for fd {fd:?} - count: {count}");
780	if dirp.is_null() || count == 0 {
781		return (-i32::from(Errno::Inval)).into();
782	}
783
784	let slice = unsafe { core::slice::from_raw_parts_mut(dirp.cast(), count) };
785
786	let obj = get_object(fd);
787	obj.map_or_else(
788		|_| (-i32::from(Errno::Inval)).into(),
789		|v| {
790			block_on((*v).getdents(slice), None)
791				.map_or_else(|e| (-i32::from(e)).into(), |cnt| cnt as i64)
792		},
793	)
794}
795
796#[hermit_macro::system(errno)]
797#[unsafe(no_mangle)]
798pub extern "C" fn sys_dup(fd: i32) -> i32 {
799	dup_object(fd).unwrap_or_else(|e| -i32::from(e))
800}
801
802#[hermit_macro::system(errno)]
803#[unsafe(no_mangle)]
804pub extern "C" fn sys_dup2(fd1: i32, fd2: i32) -> i32 {
805	dup_object2(fd1, fd2).unwrap_or_else(|e| -i32::from(e))
806}
807
808#[hermit_macro::system(errno)]
809#[unsafe(no_mangle)]
810pub extern "C" fn sys_isatty(fd: i32) -> i32 {
811	match isatty(fd) {
812		Err(e) => -i32::from(e),
813		Ok(v) => {
814			if v {
815				1
816			} else {
817				0
818			}
819		}
820	}
821}
822
823#[hermit_macro::system(errno)]
824#[unsafe(no_mangle)]
825pub unsafe extern "C" fn sys_poll(fds: *mut PollFd, nfds: usize, timeout: i32) -> i32 {
826	let slice = unsafe { core::slice::from_raw_parts_mut(fds, nfds) };
827	let timeout = if timeout >= 0 {
828		Some(core::time::Duration::from_millis(
829			timeout.try_into().unwrap(),
830		))
831	} else {
832		None
833	};
834
835	crate::fd::poll(slice, timeout).map_or_else(
836		|e| {
837			if e == Errno::Time { 0 } else { -i32::from(e) }
838		},
839		|v| v.try_into().unwrap(),
840	)
841}
842
843#[hermit_macro::system(errno)]
844#[unsafe(no_mangle)]
845pub extern "C" fn sys_eventfd(initval: u64, flags: i16) -> i32 {
846	if let Some(flags) = EventFlags::from_bits(flags) {
847		crate::fd::eventfd(initval, flags).unwrap_or_else(|e| -i32::from(e))
848	} else {
849		-i32::from(Errno::Inval)
850	}
851}
852
853#[hermit_macro::system]
854#[unsafe(no_mangle)]
855pub extern "C" fn sys_image_start_addr() -> usize {
856	crate::mm::kernel_start_address().as_usize()
857}
858
859#[cfg(test)]
860mod tests {
861	use core::ptr;
862
863	use super::*;
864
865	#[cfg(target_os = "none")]
866	#[test_case]
867	fn test_get_application_parameters() {
868		crate::env::init();
869		let (argc, argv, _envp) = get_application_parameters();
870		assert_ne!(argc, 0);
871		assert_ne!(argv, ptr::null());
872	}
873}