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;
11
12pub use self::condvar::*;
13pub use self::entropy::*;
14pub use self::futex::*;
15pub use self::processor::*;
16#[cfg(feature = "newlib")]
17pub use self::recmutex::*;
18pub use self::semaphore::*;
19pub use self::spinlock::*;
20pub use self::system::*;
21pub use self::tasks::*;
22pub use self::timer::*;
23use crate::env;
24use crate::errno::{Errno, ToErrno};
25use crate::executor::block_on;
26use crate::fd::{
27 self, AccessOption, AccessPermission, EventFlags, ObjectInterface, OpenOption, PollFd, RawFd,
28 dup_object, dup_object2, get_object, isatty, remove_object,
29};
30use crate::fs::{self, FileAttr, SeekWhence};
31#[cfg(all(target_os = "none", not(feature = "common-os")))]
32use crate::mm::ALLOCATOR;
33
34mod condvar;
35mod entropy;
36mod futex;
37#[cfg(feature = "mman")]
38pub mod mman;
39mod processor;
40#[cfg(feature = "newlib")]
41mod recmutex;
42mod semaphore;
43#[cfg(any(feature = "net", feature = "virtio-vsock"))]
44pub mod socket;
45mod spinlock;
46mod system;
47#[cfg(feature = "common-os")]
48pub(crate) mod table;
49mod tasks;
50mod timer;
51
52#[repr(C)]
53#[derive(Debug, Clone, Copy)]
54struct iovec {
56 pub iov_base: *mut u8,
58 pub iov_len: usize,
60}
61
62const IOV_MAX: usize = 1024;
63
64pub(crate) fn init() {
65 init_entropy();
66}
67
68#[cfg(all(target_os = "none", not(feature = "common-os")))]
75#[hermit_macro::system]
76#[unsafe(no_mangle)]
77pub extern "C" fn sys_alloc(size: usize, align: usize) -> *mut u8 {
78 let layout_res = Layout::from_size_align(size, align);
79 if layout_res.is_err() || size == 0 {
80 warn!("__sys_alloc called with size {size:#x}, align {align:#x} is an invalid layout!");
81 return ptr::null_mut();
82 }
83 let layout = layout_res.unwrap();
84 let ptr = unsafe { ALLOCATOR.alloc(layout) };
85
86 trace!("__sys_alloc: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
87
88 ptr
89}
90
91#[cfg(all(target_os = "none", not(feature = "common-os")))]
92#[hermit_macro::system]
93#[unsafe(no_mangle)]
94pub extern "C" fn sys_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
95 let layout_res = Layout::from_size_align(size, align);
96 if layout_res.is_err() || size == 0 {
97 warn!(
98 "__sys_alloc_zeroed called with size {size:#x}, align {align:#x} is an invalid layout!"
99 );
100 return ptr::null_mut();
101 }
102 let layout = layout_res.unwrap();
103 let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) };
104
105 trace!("__sys_alloc_zeroed: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
106
107 ptr
108}
109
110#[cfg(all(target_os = "none", not(feature = "common-os")))]
111#[hermit_macro::system]
112#[unsafe(no_mangle)]
113pub extern "C" fn sys_malloc(size: usize, align: usize) -> *mut u8 {
114 let layout_res = Layout::from_size_align(size, align);
115 if layout_res.is_err() || size == 0 {
116 warn!("__sys_malloc called with size {size:#x}, align {align:#x} is an invalid layout!");
117 return ptr::null_mut();
118 }
119 let layout = layout_res.unwrap();
120 let ptr = unsafe { ALLOCATOR.alloc(layout) };
121
122 trace!("__sys_malloc: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
123
124 ptr
125}
126
127#[cfg(all(target_os = "none", not(feature = "common-os")))]
147#[hermit_macro::system]
148#[unsafe(no_mangle)]
149pub unsafe extern "C" fn sys_realloc(
150 ptr: *mut u8,
151 size: usize,
152 align: usize,
153 new_size: usize,
154) -> *mut u8 {
155 unsafe {
156 let layout_res = Layout::from_size_align(size, align);
157 if layout_res.is_err() || size == 0 || new_size == 0 {
158 warn!(
159 "__sys_realloc called with ptr {ptr:p}, size {size:#x}, align {align:#x}, new_size {new_size:#x} is an invalid layout!"
160 );
161 return ptr::null_mut();
162 }
163 let layout = layout_res.unwrap();
164 let new_ptr = ALLOCATOR.realloc(ptr, layout, new_size);
165
166 if new_ptr.is_null() {
167 debug!(
168 "__sys_realloc failed to resize ptr {ptr:p} with size {size:#x}, align {align:#x}, new_size {new_size:#x} !"
169 );
170 } else {
171 trace!("__sys_realloc: resized memory at {ptr:p}, new address {new_ptr:p}");
172 }
173 new_ptr
174 }
175}
176
177#[cfg(all(target_os = "none", not(feature = "common-os")))]
188#[hermit_macro::system]
189#[unsafe(no_mangle)]
190pub unsafe extern "C" fn sys_dealloc(ptr: *mut u8, size: usize, align: usize) {
191 unsafe {
192 let layout_res = Layout::from_size_align(size, align);
193 if layout_res.is_err() || size == 0 {
194 warn!(
195 "__sys_dealloc called with size {size:#x}, align {align:#x} is an invalid layout!"
196 );
197 debug_assert!(layout_res.is_err(), "__sys_dealloc error: Invalid layout");
198 debug_assert_ne!(size, 0, "__sys_dealloc error: size cannot be 0");
199 } else {
200 trace!("sys_free: deallocate memory at {ptr:p} (size {size:#x})");
201 }
202 let layout = layout_res.unwrap();
203 ALLOCATOR.dealloc(ptr, layout);
204 }
205}
206
207#[cfg(all(target_os = "none", not(feature = "common-os")))]
208#[hermit_macro::system]
209#[unsafe(no_mangle)]
210pub unsafe extern "C" fn sys_free(ptr: *mut u8, size: usize, align: usize) {
211 unsafe {
212 let layout_res = Layout::from_size_align(size, align);
213 if layout_res.is_err() || size == 0 {
214 warn!("__sys_free called with size {size:#x}, align {align:#x} is an invalid layout!");
215 debug_assert!(layout_res.is_err(), "__sys_free error: Invalid layout");
216 debug_assert_ne!(size, 0, "__sys_free error: size cannot be 0");
217 } else {
218 trace!("sys_free: deallocate memory at {ptr:p} (size {size:#x})");
219 }
220 let layout = layout_res.unwrap();
221 ALLOCATOR.dealloc(ptr, layout);
222 }
223}
224
225pub(crate) fn get_application_parameters() -> (i32, *const *const u8, *const *const u8) {
226 use alloc::boxed::Box;
227 use alloc::vec::Vec;
228
229 let mut argv = Vec::new();
230
231 let name = Box::leak(Box::new("bin\0")).as_ptr();
232 argv.push(name);
233
234 let args = env::args();
235 debug!("Setting argv as: {args:?}");
236 for arg in args {
237 let ptr = Box::leak(format!("{arg}\0").into_boxed_str()).as_ptr();
238 argv.push(ptr);
239 }
240
241 let mut envv = Vec::new();
242
243 let envs = env::vars();
244 debug!("Setting envv as: {envs:?}");
245 for (key, value) in envs {
246 let ptr = Box::leak(format!("{key}={value}\0").into_boxed_str()).as_ptr();
247 envv.push(ptr);
248 }
249 envv.push(ptr::null::<u8>());
250
251 let argc = argv.len() as i32;
252 let argv = argv.leak().as_ptr();
253 let envv = if envv.len() == 1 {
255 ptr::null::<*const u8>()
256 } else {
257 envv.leak().as_ptr()
258 };
259
260 (argc, argv, envv)
261}
262
263pub(crate) fn shutdown(arg: i32) -> ! {
264 crate::arch::kernel::print_statistics();
266
267 #[cfg(feature = "uhyve")]
268 if env::is_uhyve() {
269 crate::uhyve::shutdown(arg);
270 }
271
272 panic_println!("exit status {arg}");
274
275 crate::arch::processor::shutdown(arg)
276}
277
278#[hermit_macro::system(errno)]
279#[unsafe(no_mangle)]
280pub unsafe extern "C" fn sys_unlink(name: *const c_char) -> i32 {
281 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
282
283 fs::unlink(name).map_or_else(|e| -i32::from(e), |()| 0)
284}
285
286#[hermit_macro::system(errno)]
287#[unsafe(no_mangle)]
288pub unsafe extern "C" fn sys_mkdir(name: *const c_char, mode: u32) -> i32 {
289 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
290 let Some(mode) = AccessPermission::from_bits(mode) else {
291 return -i32::from(Errno::Inval);
292 };
293
294 fs::create_dir(name, mode).map_or_else(|e| -i32::from(e), |()| 0)
295}
296
297#[hermit_macro::system(errno)]
298#[unsafe(no_mangle)]
299pub unsafe extern "C" fn sys_rmdir(name: *const c_char) -> i32 {
300 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
301
302 fs::remove_dir(name).map_or_else(|e| -i32::from(e), |()| 0)
303}
304
305#[hermit_macro::system(errno)]
306#[unsafe(no_mangle)]
307pub unsafe extern "C" fn sys_stat(name: *const c_char, stat: *mut FileAttr) -> i32 {
308 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
309
310 match fs::read_stat(name) {
311 Ok(attr) => unsafe {
312 *stat = attr;
313 0
314 },
315 Err(e) => -i32::from(e),
316 }
317}
318
319#[hermit_macro::system(errno)]
320#[unsafe(no_mangle)]
321pub unsafe extern "C" fn sys_lstat(name: *const c_char, stat: *mut FileAttr) -> i32 {
322 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
323
324 match fs::read_lstat(name) {
325 Ok(attr) => unsafe {
326 *stat = attr;
327 0
328 },
329 Err(e) => -i32::from(e),
330 }
331}
332
333#[hermit_macro::system(errno)]
334#[unsafe(no_mangle)]
335pub unsafe extern "C" fn sys_fstat(fd: RawFd, stat: *mut FileAttr) -> i32 {
336 if stat.is_null() {
337 return -i32::from(Errno::Inval);
338 }
339
340 fd::fstat(fd).map_or_else(
341 |e| -i32::from(e),
342 |v| unsafe {
343 *stat = v;
344 0
345 },
346 )
347}
348
349#[hermit_macro::system(errno)]
350#[unsafe(no_mangle)]
351pub unsafe extern "C" fn sys_opendir(name: *const c_char) -> RawFd {
352 let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
353 return -i32::from(Errno::Inval);
354 };
355
356 fs::opendir(name).unwrap_or_else(|e| -i32::from(e))
357}
358
359#[hermit_macro::system(errno)]
360#[unsafe(no_mangle)]
361pub unsafe extern "C" fn sys_open(name: *const c_char, flags: i32, mode: u32) -> RawFd {
362 let Some(flags) = OpenOption::from_bits(flags) else {
363 return -i32::from(Errno::Inval);
364 };
365 let Some(mode) = AccessPermission::from_bits(mode) else {
366 return -i32::from(Errno::Inval);
367 };
368
369 let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
370 return -i32::from(Errno::Inval);
371 };
372
373 fs::open(name, flags, mode).unwrap_or_else(|e| -i32::from(e))
374}
375
376#[hermit_macro::system]
377#[unsafe(no_mangle)]
378pub unsafe extern "C" fn sys_getcwd(buf: *mut c_char, size: usize) -> *const c_char {
379 let error = |e: Errno| {
380 e.set_errno();
381 ptr::null::<c_char>()
382 };
383
384 if size == 0 {
385 return error(Errno::Inval);
386 }
387
388 if buf.is_null() {
389 return error(Errno::Noent);
391 }
392
393 let cwd = match fs::get_cwd() {
394 Err(e) => {
395 return error(e);
396 }
397 Ok(cwd) => cwd,
398 };
399
400 let Ok(cwd) = CString::new(cwd) else {
401 return error(Errno::Noent);
402 };
403
404 if (cwd.count_bytes() + 1) > size {
405 return error(Errno::Range);
406 }
407
408 unsafe {
409 buf.copy_from(cwd.as_ptr(), size);
410 }
411
412 buf
413}
414
415#[hermit_macro::system(errno)]
416#[unsafe(no_mangle)]
417pub extern "C" fn sys_fchdir(_fd: RawFd) -> i32 {
418 -i32::from(Errno::Nosys)
419}
420
421#[hermit_macro::system(errno)]
422#[unsafe(no_mangle)]
423pub unsafe extern "C" fn sys_chdir(path: *mut c_char) -> i32 {
424 let Ok(name) = unsafe { CStr::from_ptr(path) }.to_str() else {
425 return -i32::from(Errno::Inval);
426 };
427
428 fs::set_cwd(name)
429 .map(|()| 0)
430 .unwrap_or_else(|e| -i32::from(e))
431}
432
433#[hermit_macro::system]
434#[unsafe(no_mangle)]
435pub unsafe extern "C" fn sys_umask(umask: u32) -> u32 {
436 fs::umask(AccessPermission::from_bits_truncate(umask)).bits()
437}
438
439#[hermit_macro::system(errno)]
440#[unsafe(no_mangle)]
441pub unsafe extern "C" fn sys_faccessat(
442 dirfd: RawFd,
443 name: *const c_char,
444 _mode: i32,
445 flags: i32,
446) -> i32 {
447 let Some(access_option) = AccessOption::from_bits(flags) else {
448 return -i32::from(Errno::Inval);
449 };
450
451 let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
452 return -i32::from(Errno::Inval);
453 };
454
455 const AT_SYMLINK_NOFOLLOW: i32 = 0x100;
456 const AT_FDCWD: i32 = -100;
457
458 let stat = if name.starts_with("/") || dirfd == AT_FDCWD {
459 let no_follow: bool = (flags & AT_SYMLINK_NOFOLLOW) != 0;
460
461 if no_follow {
462 fs::read_stat(name)
463 } else {
464 fs::read_lstat(name)
465 }
466 } else {
467 warn!("faccessat with directory relative to fd is not implemented!");
468 return -i32::from(Errno::Nosys);
469 };
470
471 match stat {
472 Err(e) => -i32::from(e),
473 Ok(stat) if access_option.can_access(stat.st_mode) => 0,
474 Ok(_) => -i32::from(Errno::Acces),
475 }
476}
477
478#[hermit_macro::system(errno)]
479#[unsafe(no_mangle)]
480pub unsafe extern "C" fn sys_access(name: *const c_char, flags: i32) -> i32 {
481 let Some(access_option) = AccessOption::from_bits(flags) else {
482 return -i32::from(Errno::Inval);
483 };
484
485 if access_option.contains(AccessOption::F_OK) && access_option != AccessOption::F_OK {
486 return -i32::from(Errno::Inval);
487 }
488
489 let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else {
490 return -i32::from(Errno::Inval);
491 };
492
493 match fs::read_lstat(name) {
494 Err(e) => -i32::from(e),
495 Ok(stat) if access_option.can_access(stat.st_mode) => 0,
496 Ok(_) => -i32::from(Errno::Acces),
497 }
498}
499
500#[hermit_macro::system(errno)]
501#[unsafe(no_mangle)]
502pub unsafe extern "C" fn sys_fchmod(fd: RawFd, mode: u32) -> i32 {
503 let Some(access_permission) = AccessPermission::from_bits(mode) else {
504 return -i32::from(Errno::Inval);
505 };
506
507 fd::chmod(fd, access_permission)
508 .map(|()| 0)
509 .unwrap_or_else(|e| -i32::from(e))
510}
511
512#[hermit_macro::system(errno)]
513#[unsafe(no_mangle)]
514pub extern "C" fn sys_close(fd: RawFd) -> i32 {
515 let obj = remove_object(fd);
516 obj.map_or_else(|e| -i32::from(e), |_| 0)
517}
518
519#[hermit_macro::system(errno)]
520#[unsafe(no_mangle)]
521pub unsafe extern "C" fn sys_read(fd: RawFd, buf: *mut u8, len: usize) -> isize {
522 let slice = unsafe { slice::from_raw_parts_mut(buf.cast(), len) };
523 fd::read(fd, slice).map_or_else(
524 |e| isize::try_from(-i32::from(e)).unwrap(),
525 |v| v.try_into().unwrap(),
526 )
527}
528
529#[hermit_macro::system(errno)]
545#[unsafe(no_mangle)]
546pub unsafe extern "C" fn sys_readv(fd: i32, iov: *const iovec, iovcnt: usize) -> isize {
547 if !(0..=IOV_MAX).contains(&iovcnt) {
548 return (-i32::from(Errno::Inval)).try_into().unwrap();
549 }
550
551 let mut read_bytes: isize = 0;
552 let iovec_buffers = unsafe { slice::from_raw_parts(iov, iovcnt) };
553
554 for iovec_buf in iovec_buffers {
555 let buf =
556 unsafe { slice::from_raw_parts_mut(iovec_buf.iov_base.cast(), iovec_buf.iov_len) };
557
558 let len = fd::read(fd, buf).map_or_else(
559 |e| isize::try_from(-i32::from(e)).unwrap(),
560 |v| v.try_into().unwrap(),
561 );
562
563 if len < 0 {
564 return len;
565 }
566
567 read_bytes += len;
568
569 if len < isize::try_from(iovec_buf.iov_len).unwrap() {
570 return read_bytes;
571 }
572 }
573
574 read_bytes
575}
576
577unsafe fn write(fd: RawFd, buf: *const u8, len: usize) -> isize {
578 let slice = unsafe { slice::from_raw_parts(buf, len) };
579 fd::write(fd, slice).map_or_else(
580 |e| isize::try_from(-i32::from(e)).unwrap(),
581 |v| v.try_into().unwrap(),
582 )
583}
584
585#[hermit_macro::system(errno)]
586#[unsafe(no_mangle)]
587pub unsafe extern "C" fn sys_write(fd: RawFd, buf: *const u8, len: usize) -> isize {
588 unsafe { write(fd, buf, len) }
589}
590
591#[hermit_macro::system(errno)]
592#[unsafe(no_mangle)]
593pub unsafe extern "C" fn sys_ftruncate(fd: RawFd, size: usize) -> i32 {
594 fd::truncate(fd, size).map_or_else(|e| -i32::from(e), |()| 0)
595}
596
597#[hermit_macro::system(errno)]
598#[unsafe(no_mangle)]
599pub unsafe extern "C" fn sys_truncate(path: *const c_char, size: usize) -> i32 {
600 let Ok(path) = unsafe { CStr::from_ptr(path) }.to_str() else {
601 return -i32::from(Errno::Inval);
602 };
603
604 fs::truncate(path, size).map_or_else(|e| -i32::from(e), |()| 0)
605}
606
607#[hermit_macro::system(errno)]
623#[unsafe(no_mangle)]
624pub unsafe extern "C" fn sys_writev(fd: RawFd, iov: *const iovec, iovcnt: usize) -> isize {
625 if !(0..=IOV_MAX).contains(&iovcnt) {
626 return (-i32::from(Errno::Inval)).try_into().unwrap();
627 }
628
629 let mut written_bytes: isize = 0;
630 let iovec_buffers = unsafe { slice::from_raw_parts(iov, iovcnt) };
631
632 for iovec_buf in iovec_buffers {
633 let buf = unsafe { slice::from_raw_parts(iovec_buf.iov_base, iovec_buf.iov_len) };
634
635 let len = fd::write(fd, buf).map_or_else(
636 |e| isize::try_from(-i32::from(e)).unwrap(),
637 |v| v.try_into().unwrap(),
638 );
639
640 if len < 0 {
641 return len;
642 }
643
644 written_bytes += len;
645
646 if len < isize::try_from(iovec_buf.iov_len).unwrap() {
647 return written_bytes;
648 }
649 }
650
651 written_bytes
652}
653
654#[hermit_macro::system(errno)]
655#[unsafe(no_mangle)]
656pub unsafe extern "C" fn sys_ioctl(fd: RawFd, cmd: i32, argp: *mut core::ffi::c_void) -> i32 {
657 const FIONBIO: i32 = 0x8008_667eu32 as i32;
658
659 if cmd == FIONBIO {
660 let value = unsafe { *(argp as *const i32) };
661 let status_flags = if value != 0 {
662 fd::StatusFlags::O_NONBLOCK
663 } else {
664 fd::StatusFlags::empty()
665 };
666
667 let obj = get_object(fd);
668 obj.map_or_else(
669 |e| -i32::from(e),
670 |v| {
671 block_on(
672 async { v.write().await.set_status_flags(status_flags).await },
673 None,
674 )
675 .map_or_else(|e| -i32::from(e), |()| 0)
676 },
677 )
678 } else {
679 -i32::from(Errno::Inval)
680 }
681}
682
683#[hermit_macro::system(errno)]
685#[unsafe(no_mangle)]
686pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 {
687 const F_SETFD: i32 = 2;
688 const F_GETFL: i32 = 3;
689 const F_SETFL: i32 = 4;
690 const FD_CLOEXEC: i32 = 1;
691
692 if cmd == F_SETFD && arg == FD_CLOEXEC {
693 0
694 } else if cmd == F_GETFL {
695 let obj = get_object(fd);
696 obj.map_or_else(
697 |e| -i32::from(e),
698 |v| {
699 block_on(async { v.read().await.status_flags().await }, None)
700 .map_or_else(|e| -i32::from(e), |status_flags| status_flags.bits())
701 },
702 )
703 } else if cmd == F_SETFL {
704 let obj = get_object(fd);
705 obj.map_or_else(
706 |e| -i32::from(e),
707 |v| {
708 block_on(
709 async {
710 v.write()
711 .await
712 .set_status_flags(fd::StatusFlags::from_bits_retain(arg))
713 .await
714 },
715 None,
716 )
717 .map_or_else(|e| -i32::from(e), |()| 0)
718 },
719 )
720 } else {
721 -i32::from(Errno::Inval)
722 }
723}
724
725#[hermit_macro::system(errno)]
726#[unsafe(no_mangle)]
727pub extern "C" fn sys_lseek(fd: RawFd, offset: isize, whence: i32) -> isize {
728 let whence = u8::try_from(whence).unwrap();
729 let whence = SeekWhence::try_from(whence).unwrap();
730 fd::lseek(fd, offset, whence).unwrap_or_else(|e| isize::try_from(-i32::from(e)).unwrap())
731}
732
733#[repr(C)]
734pub struct Dirent64 {
735 pub d_ino: u64,
737 pub d_off: i64,
739 pub d_reclen: u16,
741 pub d_type: fs::FileType,
743 pub d_name: PhantomData<c_char>,
745}
746impl Dirent64 {
747 #[allow(dead_code)]
753 unsafe fn display<'a>(&'a self) -> Dirent64Display<'a> {
754 unsafe { Dirent64Display::new(self) }
755 }
756}
757
758mod dirent_display {
759 use core::ffi::{CStr, c_char};
760 use core::fmt;
761
762 use super::Dirent64;
763
764 pub(super) struct Dirent64Display<'a> {
768 dirent: &'a Dirent64,
769 }
770
771 impl<'a> Dirent64Display<'a> {
772 pub(super) unsafe fn new(dirent: &'a Dirent64) -> Self {
775 Self { dirent }
776 }
777 }
778
779 impl fmt::Debug for Dirent64Display<'_> {
780 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781 let cstr = unsafe { CStr::from_ptr((&raw const self.dirent.d_name).cast::<c_char>()) };
782
783 f.debug_struct("Dirent64")
784 .field("d_ino", &self.dirent.d_ino)
785 .field("d_off", &self.dirent.d_off)
786 .field("d_reclen", &self.dirent.d_reclen)
787 .field("d_type", &self.dirent.d_type)
788 .field("d_name", &cstr)
789 .finish()
790 }
791 }
792}
793
794#[hermit_macro::system(errno)]
811#[unsafe(no_mangle)]
812pub unsafe extern "C" fn sys_getdents64(fd: RawFd, dirp: *mut Dirent64, count: usize) -> i64 {
813 debug!("getdents for fd {fd:?} - count: {count}");
814 if dirp.is_null() || count == 0 {
815 return (-i32::from(Errno::Inval)).into();
816 }
817
818 let slice = unsafe { slice::from_raw_parts_mut(dirp.cast(), count) };
819
820 let obj = get_object(fd);
821 obj.map_or_else(
822 |_| (-i32::from(Errno::Inval)).into(),
823 |v| {
824 block_on(async { v.read().await.getdents(slice).await }, None)
825 .map_or_else(|e| (-i32::from(e)).into(), |cnt| cnt as i64)
826 },
827 )
828}
829
830#[hermit_macro::system(errno)]
831#[unsafe(no_mangle)]
832pub extern "C" fn sys_dup(fd: i32) -> i32 {
833 dup_object(fd).unwrap_or_else(|e| -i32::from(e))
834}
835
836#[hermit_macro::system(errno)]
837#[unsafe(no_mangle)]
838pub extern "C" fn sys_dup2(fd1: i32, fd2: i32) -> i32 {
839 dup_object2(fd1, fd2).unwrap_or_else(|e| -i32::from(e))
840}
841
842#[hermit_macro::system(errno)]
843#[unsafe(no_mangle)]
844pub extern "C" fn sys_isatty(fd: i32) -> i32 {
845 match isatty(fd) {
846 Err(e) => -i32::from(e),
847 Ok(v) => {
848 if v {
849 1
850 } else {
851 0
852 }
853 }
854 }
855}
856
857#[hermit_macro::system(errno)]
858#[unsafe(no_mangle)]
859pub unsafe extern "C" fn sys_poll(fds: *mut PollFd, nfds: usize, timeout: i32) -> i32 {
860 let slice = unsafe { slice::from_raw_parts_mut(fds, nfds) };
861 let timeout = if timeout >= 0 {
862 Some(core::time::Duration::from_millis(
863 timeout.try_into().unwrap(),
864 ))
865 } else {
866 None
867 };
868
869 fd::poll(slice, timeout).map_or_else(
870 |e| {
871 if e == Errno::Time { 0 } else { -i32::from(e) }
872 },
873 |v| v.try_into().unwrap(),
874 )
875}
876
877#[hermit_macro::system(errno)]
878#[unsafe(no_mangle)]
879pub extern "C" fn sys_eventfd(initval: u64, flags: i16) -> i32 {
880 let Some(flags) = EventFlags::from_bits(flags) else {
881 return -i32::from(Errno::Inval);
882 };
883
884 fd::eventfd(initval, flags).unwrap_or_else(|e| -i32::from(e))
885}
886
887#[hermit_macro::system]
888#[unsafe(no_mangle)]
889pub extern "C" fn sys_image_start_addr() -> usize {
890 crate::mm::kernel_start_address().as_usize()
891}
892
893#[cfg(test)]
894mod tests {
895 use super::*;
896
897 #[cfg(target_os = "none")]
898 #[test_case]
899 fn test_get_application_parameters() {
900 env::init();
901 let (argc, argv, _envp) = get_application_parameters();
902 assert_ne!(argc, 0);
903 assert_ne!(argv, ptr::null());
904 }
905}