use core::fmt;
use core::mem;
use thiserror::Error;
#[cfg(target_arch = "x86_64")]
#[path = "arch_x86_64.rs"]
pub mod arch_x86_64;
#[cfg(target_arch = "aarch64")]
#[path = "arch_aarch64.rs"]
pub mod arch_aarch64;
#[cfg(target_arch = "x86_64")]
use arch_x86_64 as arch;
#[cfg(target_arch = "aarch64")]
use arch_aarch64 as arch;
pub use arch::SysCall;
use arch::syscall;
pub mod constants {
pub const PAGE_SIZE: usize = 4096;
pub const TCGETS: u64 = 0x5401;
pub const TCSETS: u64 = 0x5402;
}
pub type Result<T> = core::result::Result<T, SyscallError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("{errno}")]
pub struct SyscallError {
errno: Errno,
}
impl SyscallError {
#[inline]
pub const fn new(errno: Errno) -> Self {
Self { errno }
}
#[inline]
pub const fn errno(&self) -> Errno {
self.errno
}
#[inline]
pub const fn raw(&self) -> i32 {
self.errno as i32
}
}
impl From<Errno> for SyscallError {
#[inline]
fn from(errno: Errno) -> Self {
Self { errno }
}
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum Errno {
Perm = 1,
NoEnt = 2,
Srch = 3,
Intr = 4,
Io = 5,
NxIo = 6,
TooBig = 7,
NoExec = 8,
BadF = 9,
Child = 10,
Again = 11,
NoMem = 12,
Acces = 13,
Fault = 14,
NotBlk = 15,
Busy = 16,
Exist = 17,
XDev = 18,
NoDev = 19,
NotDir = 20,
IsDir = 21,
Inval = 22,
NFile = 23,
MFile = 24,
NotTy = 25,
TxtBsy = 26,
FBig = 27,
NoSpc = 28,
SPipe = 29,
RoFs = 30,
MLink = 31,
Pipe = 32,
Dom = 33,
Range = 34,
DeadLk = 35,
NameTooLong = 36,
NoLck = 37,
NoSys = 38,
NotEmpty = 39,
Loop = 40,
NoMsg = 42,
IdRm = 43,
NetDown = 100,
NetUnreach = 101,
NetReset = 102,
ConnAborted = 103,
ConnReset = 104,
NoBufs = 105,
IsConn = 106,
NotConn = 107,
Shutdown = 108,
TooManyRefs = 109,
TimedOut = 110,
ConnRefused = 111,
HostDown = 112,
HostUnreach = 113,
Already = 114,
InProgress = 115,
Stale = 116,
}
impl Errno {
#[inline]
pub fn from_raw(errno: i32) -> Option<Self> {
match errno {
1..=40 | 42..=43 | 100..=116 => {
Some(unsafe { core::mem::transmute::<i32, Errno>(errno) })
}
_ => None,
}
}
#[inline]
pub const fn as_raw(&self) -> i32 {
*self as i32
}
#[inline]
pub const fn should_retry(&self) -> bool {
matches!(self, Self::Again | Self::Intr)
}
#[inline]
pub const fn is_temporary(&self) -> bool {
matches!(self, Self::Again | Self::Intr | Self::InProgress)
}
}
impl fmt::Display for Errno {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = match self {
Self::Perm => "Operation not permitted",
Self::NoEnt => "No such file or directory",
Self::Srch => "No such process",
Self::Intr => "Interrupted system call",
Self::Io => "I/O error",
Self::NxIo => "No such device or address",
Self::TooBig => "Argument list too long",
Self::NoExec => "Exec format error",
Self::BadF => "Bad file descriptor",
Self::Child => "No child processes",
Self::Again => "Resource temporarily unavailable",
Self::NoMem => "Out of memory",
Self::Acces => "Permission denied",
Self::Fault => "Bad address",
Self::NotBlk => "Block device required",
Self::Busy => "Device or resource busy",
Self::Exist => "File exists",
Self::XDev => "Cross-device link",
Self::NoDev => "No such device",
Self::NotDir => "Not a directory",
Self::IsDir => "Is a directory",
Self::Inval => "Invalid argument",
Self::NFile => "File table overflow",
Self::MFile => "Too many open files",
Self::NotTy => "Not a typewriter",
Self::TxtBsy => "Text file busy",
Self::FBig => "File too large",
Self::NoSpc => "No space left on device",
Self::SPipe => "Illegal seek",
Self::RoFs => "Read-only file system",
Self::MLink => "Too many links",
Self::Pipe => "Broken pipe",
Self::Dom => "Math argument out of domain",
Self::Range => "Math result not representable",
Self::DeadLk => "Resource deadlock would occur",
Self::NameTooLong => "File name too long",
Self::NoLck => "No record locks available",
Self::NoSys => "Function not implemented",
Self::NotEmpty => "Directory not empty",
Self::Loop => "Too many symbolic links encountered",
Self::NoMsg => "No message of desired type",
Self::IdRm => "Identifier removed",
Self::NetDown => "Network is down",
Self::NetUnreach => "Network is unreachable",
Self::NetReset => "Network dropped connection on reset",
Self::ConnAborted => "Software caused connection abort",
Self::ConnReset => "Connection reset by peer",
Self::NoBufs => "No buffer space available",
Self::IsConn => "Transport endpoint is already connected",
Self::NotConn => "Transport endpoint is not connected",
Self::Shutdown => "Cannot send after transport endpoint shutdown",
Self::TooManyRefs => "Too many references: cannot splice",
Self::TimedOut => "Connection timed out",
Self::ConnRefused => "Connection refused",
Self::HostDown => "Host is down",
Self::HostUnreach => "No route to host",
Self::Already => "Operation already in progress",
Self::InProgress => "Operation now in progress",
Self::Stale => "Stale file handle",
};
write!(f, "{} ({})", desc, *self as i32)
}
}
#[inline]
pub fn check_ret(ret: i64) -> Result<i64> {
if ret < 0 {
let errno = (-ret) as i32;
Errno::from_raw(errno)
.map(SyscallError::from)
.map(Err)
.unwrap_or(Err(SyscallError::from(Errno::NoSys)))
} else {
Ok(ret)
}
}
pub trait Flag: Copy + Clone {
fn as_raw(&self) -> i32;
fn combine(self, other: Self) -> i32 {
self.as_raw() | other.as_raw()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Flags<T: Flag> {
value: i32,
_phantom: core::marker::PhantomData<T>,
}
impl<T: Flag> Flags<T> {
pub const fn new() -> Self {
Self {
value: 0,
_phantom: core::marker::PhantomData,
}
}
pub const fn from_raw(value: i32) -> Self {
Self {
value,
_phantom: core::marker::PhantomData,
}
}
pub fn with(mut self, flag: T) -> Self {
self.value |= flag.as_raw();
self
}
pub const fn as_raw(&self) -> i32 {
self.value
}
pub fn contains(&self, flag: T) -> bool {
(self.value & flag.as_raw()) != 0
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct TimeSpec {
pub tv_sec: i64,
pub tv_nsec: i64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct IoVec {
pub iov_base: *mut u8,
pub iov_len: usize,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SockAddr {
pub sa_family: u16,
pub sa_data: [u8; 14],
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpenFlag {
RdOnly = 0,
WrOnly = 1,
RdWr = 2,
Creat = 0x40,
Excl = 0x80,
NoFollow = 0x20000,
NonBlock = 0x800,
CloExec = 0x80000,
}
impl Flag for OpenFlag {
fn as_raw(&self) -> i32 {
*self as i32
}
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtFlag {
None = 0,
Read = 1,
Write = 2,
Exec = 4,
}
impl Flag for ProtFlag {
fn as_raw(&self) -> i32 {
*self as i32
}
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MapFlag {
Shared = 0x01,
Private = 0x02,
Fixed = 0x10,
Anonymous = 0x20,
Populate = 0x8000,
}
impl Flag for MapFlag {
fn as_raw(&self) -> i32 {
*self as i32
}
}
#[inline]
pub unsafe fn read(fd: i32, buf: *mut u8, count: usize) -> Result<usize> {
check_ret(syscall!(SysCall::Read, fd, buf, count)).map(|n| n as usize)
}
#[inline]
pub unsafe fn write(fd: i32, buf: *const u8, count: usize) -> Result<usize> {
check_ret(syscall!(SysCall::Write, fd, buf, count)).map(|n| n as usize)
}
#[inline]
pub unsafe fn close(fd: i32) -> Result<()> {
check_ret(syscall!(SysCall::Close, fd)).map(|_| ())
}
#[inline]
pub unsafe fn mmap(
addr: *mut u8,
len: usize,
prot: i32,
flags: i32,
fd: i32,
offset: i64,
) -> Result<*mut u8> {
let ret = syscall!(SysCall::Mmap, addr, len, prot, flags, fd, offset);
if ret == -1 {
Err(SyscallError::from(Errno::NoMem))
} else {
Ok(ret as *mut u8)
}
}
#[inline]
pub unsafe fn munmap(addr: *mut u8, len: usize) -> Result<()> {
check_ret(syscall!(SysCall::Munmap, addr, len)).map(|_| ())
}
#[inline]
pub unsafe fn madvise(addr: *mut u8, len: usize, advice: i32) -> Result<()> {
check_ret(syscall!(SysCall::Madvise, addr, len, advice)).map(|_| ())
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
#[inline]
pub unsafe fn io_uring_setup(entries: u32, params: *mut u8) -> Result<i32> {
check_ret(syscall!(SysCall::IoUringSetup, entries, params)).map(|fd| fd as i32)
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
#[inline]
pub unsafe fn io_uring_enter(
fd: i32,
to_submit: u32,
min_complete: u32,
flags: u32,
arg: *const u8,
argsz: usize,
) -> Result<u32> {
check_ret(syscall!(
SysCall::IoUringEnter,
fd,
to_submit,
min_complete,
flags,
arg,
argsz
))
.map(|n| n as u32)
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
#[inline]
pub unsafe fn io_uring_register(fd: i32, opcode: u32, arg: *const u8, nr_args: u32) -> Result<()> {
check_ret(syscall!(SysCall::IoUringRegister, fd, opcode, arg, nr_args)).map(|_| ())
}
#[inline]
pub unsafe fn bind(sockfd: i32, addr: *const SockAddr, addrlen: u32) -> Result<()> {
check_ret(syscall!(SysCall::Bind, sockfd, addr, addrlen)).map(|_| ())
}
#[inline]
pub unsafe fn listen(sockfd: i32, backlog: i32) -> Result<()> {
check_ret(syscall!(SysCall::Listen, sockfd, backlog)).map(|_| ())
}
#[inline]
pub unsafe fn ftruncate(fd: i32, length: i64) -> Result<()> {
check_ret(syscall!(SysCall::Ftruncate, fd, length)).map(|_| ())
}