#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Retval(usize);
impl Retval {
pub const fn from_usize(v: usize) -> Retval {
Retval(v)
}
pub const fn as_usize(self) -> usize {
self.0
}
pub const fn is_error(self) -> bool {
self.0 > core::usize::MAX - 4096
}
pub const fn is_success(self) -> bool {
!self.is_error()
}
pub unsafe fn error_unchecked(self) -> usize {
!self.0 + 1
}
pub fn error(self) -> usize {
if self.is_error() {
unsafe { self.error_unchecked() }
} else {
panic!("called `r_linux::syscall::raw::Retval::error()` on a success value")
}
}
pub unsafe fn unwrap_unchecked(self) -> usize {
self.0
}
pub fn unwrap(self) -> usize {
if self.is_success() {
unsafe { self.unwrap_unchecked() }
} else {
panic!("called `r_linux::syscall::raw::Retval::unwrap()` on an error value")
}
}
pub fn to_result(self) -> Result<usize, usize> {
if self.is_error() {
Err(!self.0 + 1)
} else {
Ok(self.0)
}
}
}
pub unsafe fn syscall0(
nr: usize,
) -> Retval {
Retval::from_usize(
super::arch::native::syscall::syscall0(
nr,
)
)
}
pub unsafe fn syscall1(
nr: usize,
arg0: usize,
) -> Retval {
Retval::from_usize(
super::arch::native::syscall::syscall1(
nr,
arg0,
)
)
}
pub unsafe fn syscall2(
nr: usize,
arg0: usize,
arg1: usize,
) -> Retval {
Retval::from_usize(
super::arch::native::syscall::syscall2(
nr,
arg0,
arg1,
)
)
}
pub unsafe fn syscall3(
nr: usize,
arg0: usize,
arg1: usize,
arg2: usize,
) -> Retval {
Retval::from_usize(
super::arch::native::syscall::syscall3(
nr,
arg0,
arg1,
arg2,
)
)
}
pub unsafe fn syscall4(
nr: usize,
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
) -> Retval {
Retval::from_usize(
super::arch::native::syscall::syscall4(
nr,
arg0,
arg1,
arg2,
arg3,
)
)
}
pub unsafe fn syscall5(
nr: usize,
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
) -> Retval {
Retval::from_usize(
super::arch::native::syscall::syscall5(
nr,
arg0,
arg1,
arg2,
arg3,
arg4,
)
)
}
pub unsafe fn syscall6(
nr: usize,
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
arg5: usize,
) -> Retval {
Retval::from_usize(
super::arch::native::syscall::syscall6(
nr,
arg0,
arg1,
arg2,
arg3,
arg4,
arg5,
)
)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn retval_check() {
let success_values = [
0, 1, 2, 3,
254, 255, 256, 257,
65534, 65535, 65536, 65537,
core::usize::MAX / 2,
core::usize::MAX / 2 + 1,
core::usize::MAX - 4097,
core::usize::MAX - 4096,
];
for v in &success_values {
let r = Retval::from_usize(*v);
assert_eq!(r, Retval(*v));
assert_eq!(r.as_usize(), *v);
assert_eq!(r.is_success(), true);
assert_eq!(r.is_error(), false);
assert_eq!(unsafe { r.unwrap_unchecked() }, *v);
assert_eq!(r.unwrap(), *v);
assert_eq!(r.to_result(), Ok(*v));
}
let error_values = [
(4096, core::usize::MAX - 4095),
(4095, core::usize::MAX - 4094),
(4094, core::usize::MAX - 4093),
(4093, core::usize::MAX - 4092),
(4, core::usize::MAX - 3),
(3, core::usize::MAX - 2),
(2, core::usize::MAX - 1),
(1, core::usize::MAX),
];
for (c, v) in &error_values {
let r = Retval::from_usize(*v);
assert_eq!(r, Retval(*v));
assert_eq!(r.as_usize(), *v);
assert_eq!(r.is_success(), false);
assert_eq!(r.is_error(), true);
assert_eq!(unsafe { r.error_unchecked() }, *c);
assert_eq!(r.error(), *c);
assert_eq!(r.to_result(), Err(*c));
}
let r = Retval::from_usize(71);
let copy = r;
assert_ne!(© as *const _, &r as *const _);
assert_eq!(copy, r);
let clone = r.clone();
assert_ne!(&clone as *const _, &r as *const _);
assert_eq!(clone, r);
let (com1, com2) = (Retval::from_usize(71), Retval::from_usize(72));
assert_eq!(r, com1);
assert_ne!(r, com2);
}
#[test]
#[should_panic]
fn retval_error_panic() {
Retval::from_usize(0).error();
}
#[test]
#[should_panic]
fn retval_unwrap_panic() {
Retval::from_usize(core::usize::MAX).unwrap();
}
#[test]
fn syscall0_check() {
let r0 = unsafe { syscall0(crate::syscall::arch::native::nr::GETPID) };
assert_eq!(r0.unwrap() as u32, std::process::id());
}
#[test]
fn syscall1_check() {
let mut p0: [u32; 2] = [0, 0];
let r0 = unsafe {
syscall2(
crate::syscall::arch::native::nr::PIPE2,
p0.as_mut_ptr() as usize,
0,
).unwrap()
};
assert_eq!(r0, 0);
assert!(p0[0] > 2);
assert!(p0[1] > 2);
assert_ne!(p0[0], p0[1]);
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
p0[0] as usize,
).unwrap()
};
assert_eq!(r0, 0);
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
p0[1] as usize,
).unwrap()
};
assert_eq!(r0, 0);
}
#[test]
fn syscall2_check() {
let mut p0: [u32; 2] = [0, 0];
let r0 = unsafe {
syscall2(
crate::syscall::arch::native::nr::PIPE2,
p0.as_mut_ptr() as usize,
0,
).unwrap()
};
assert_eq!(r0, 0);
assert!(p0[0] > 2);
assert!(p0[1] > 2);
assert_ne!(p0[0], p0[1]);
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
p0[0] as usize,
).unwrap()
};
assert_eq!(r0, 0);
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
p0[1] as usize,
).unwrap()
};
assert_eq!(r0, 0);
}
#[test]
fn syscall3_check() {
let mut p0: [u32; 2] = [0, 0];
let mut b0: [u8; 16] = [0; 16];
let r0 = unsafe {
syscall2(
crate::syscall::arch::native::nr::PIPE2,
p0.as_mut_ptr() as usize,
0,
).unwrap()
};
assert_eq!(r0, 0);
assert!(p0[0] > 2);
assert!(p0[1] > 2);
assert_ne!(p0[0], p0[1]);
let r0 = unsafe {
syscall3(
crate::syscall::arch::native::nr::WRITE,
p0[1] as usize,
"foobar".as_ptr() as usize,
6 as usize,
)
.unwrap()
};
assert_eq!(r0, 6);
let r0 = unsafe {
syscall3(
crate::syscall::arch::native::nr::READ,
p0[0] as usize,
b0.as_mut_ptr() as usize,
6 as usize,
)
.unwrap()
};
assert_eq!(r0, 6);
assert_eq!(core::str::from_utf8(&b0[..6]), Ok("foobar"));
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
p0[0] as usize,
).unwrap()
};
assert_eq!(r0, 0);
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
p0[1] as usize,
).unwrap()
};
assert_eq!(r0, 0);
}
#[test]
fn syscall4_check() {
let mut b0: [u8; 128] = [0; 128];
let f0 = unsafe {
syscall2(
crate::syscall::arch::native::nr::MEMFD_CREATE,
"foobar\x00".as_ptr() as usize,
0,
).unwrap()
};
assert!(f0 > 2);
let r0 = unsafe {
syscall4(
crate::syscall::arch::native::nr::READLINKAT,
core::usize::MAX - 100 + 1, format!("/proc/self/fd/{}\x00", f0).as_str().as_ptr() as usize,
b0.as_mut_ptr() as usize,
128 - 1,
)
.unwrap()
};
assert_eq!(r0, 23);
assert_eq!(
core::str::from_utf8(&b0[..23]).unwrap(),
"/memfd:foobar (deleted)",
);
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
f0,
).unwrap()
};
assert_eq!(r0, 0);
}
#[test]
fn syscall5_check() {
let mut b0: [u32; 1024] = [0; 1024];
let r0 = unsafe {
syscall5(
crate::syscall::arch::native::nr::STATX,
core::usize::MAX - 100 + 1, "/proc/self/fd/0".as_ptr() as usize,
0x100, 0x1, b0.as_mut_ptr() as usize,
)
.unwrap()
};
assert_eq!(r0, 0);
assert_ne!(b0[0] & 0x1, 0);
assert_eq!(
unsafe {
core::ptr::read_unaligned(
(b0.as_ptr() as *const u16).offset(14)
)
} & 0o170000, 0o120000, );
}
#[test]
fn syscall6_check() {
let mut b0: [u8; 128] = [0; 128];
let f0 = unsafe {
syscall2(
crate::syscall::arch::native::nr::MEMFD_CREATE,
"foobar\x00".as_ptr() as usize,
0,
).unwrap()
};
let f1 = unsafe {
syscall2(
crate::syscall::arch::native::nr::MEMFD_CREATE,
"foobar\x00".as_ptr() as usize,
0,
).unwrap()
};
assert!(f0 > 2);
assert!(f1 > 2);
assert_ne!(f0, f1);
let r0 = unsafe {
syscall3(
crate::syscall::arch::native::nr::WRITE,
f0 as usize,
"foobar".as_ptr() as usize,
6 as usize,
)
.unwrap()
};
assert_eq!(r0, 6);
let r0 = unsafe {
syscall3(
crate::syscall::arch::native::nr::LSEEK,
f0 as usize,
0 as usize,
0 as usize,
).unwrap()
};
assert_eq!(r0, 0);
let r0 = unsafe {
syscall6(
crate::syscall::arch::native::nr::COPY_FILE_RANGE,
f0 as usize,
0 as usize,
f1 as usize,
0 as usize,
6 as usize,
0 as usize,
)
.unwrap()
};
assert_eq!(r0, 6);
let r0 = unsafe {
syscall3(
crate::syscall::arch::native::nr::LSEEK,
f1 as usize,
0 as usize,
0 as usize,
).unwrap()
};
assert_eq!(r0, 0);
let r0 = unsafe {
syscall3(
crate::syscall::arch::native::nr::READ,
f1 as usize,
b0.as_mut_ptr() as usize,
6 as usize,
)
.unwrap()
};
assert_eq!(r0, 6);
assert_eq!(core::str::from_utf8(&b0[..6]), Ok("foobar"));
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
f1,
).unwrap()
};
assert_eq!(r0, 0);
let r0 = unsafe {
syscall1(
crate::syscall::arch::native::nr::CLOSE,
f0,
).unwrap()
};
assert_eq!(r0, 0);
}
}