diff --git a/src/error.rs b/src/error.rs
index 105e6599d70d792f4815cf167033118a4effdf56..21d470b3b88ddb1b639fe283627bc32c59d8185d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -14,6 +14,8 @@ impl Errno {
     }
 }
 
+pub type Result<T, E = Errno> = core::result::Result<T, E>;
+
 #[cfg(target_os = "redox")]
 impl From<syscall::Error> for Errno {
     #[inline]
diff --git a/src/fs.rs b/src/fs.rs
index db427038f72256a531eab57c45980254609fde55..76b31c307e2b424921d913e96b6d5f5d725294a6 100644
--- a/src/fs.rs
+++ b/src/fs.rs
@@ -46,10 +46,7 @@ impl File {
     }
 
     pub fn try_clone(&self) -> io::Result<Self> {
-        match Sys::dup(self.fd) {
-            -1 => Err(io::last_os_error()),
-            ok => Ok(Self::new(ok)),
-        }
+        Ok(Self::new(Sys::dup(self.fd)?))
     }
 
     /// Create a new file pointing to the same underlying descriptor. This file
@@ -93,10 +90,7 @@ impl io::Seek for &File {
             io::SeekFrom::End(end) => (end as off_t, SEEK_END),
         };
 
-        match Sys::lseek(self.fd, offset, whence) {
-            -1 => Err(io::last_os_error()),
-            ok => Ok(ok as u64),
-        }
+        Ok(Sys::lseek(self.fd, offset, whence)? as u64)
     }
 }
 
diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs
index 436ac1357503687ea8a4a0720c10b2d776c02352..7c892cfc9dd1b493c4538b1625bd94c8055b3202 100644
--- a/src/header/dirent/mod.rs
+++ b/src/header/dirent/mod.rs
@@ -6,7 +6,7 @@ use core::{mem, ptr};
 use crate::{
     c_str::CStr,
     c_vec::CVec,
-    error::{Errno, ResultExtPtrMut},
+    error::{Errno, ResultExt, ResultExtPtrMut},
     fs::File,
     header::{fcntl, stdlib, string},
     platform::{self, types::*, Pal, Sys},
@@ -102,7 +102,7 @@ impl DIR {
         self.buf_offset = 0;
         self.opaque_offset = 0;
     }
-    fn close(mut self) -> c_int {
+    fn close(mut self) -> Result<(), Errno> {
         // Reference files aren't closed when dropped
         self.file.reference = true;
 
@@ -152,7 +152,7 @@ pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
 
 #[no_mangle]
 pub extern "C" fn closedir(dir: Box<DIR>) -> c_int {
-    dir.close()
+    dir.close().map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs
index d134bad39eff3f6c598321d2c6e47ff9d0a6c79b..1379a149e55622365f25ccdd40d2fb0084f8b921 100644
--- a/src/header/netdb/lookup.rs
+++ b/src/header/netdb/lookup.rs
@@ -50,7 +50,9 @@ pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
         let dns_addr = unsafe { mem::transmute::<[u8; 4], u32>(dns_arr) };
 
         let mut timespec = timespec::default();
-        Sys::clock_gettime(time::constants::CLOCK_REALTIME, &mut timespec);
+        unsafe {
+            Sys::clock_gettime(time::constants::CLOCK_REALTIME, &mut timespec);
+        }
         let tid = (timespec.tv_nsec >> 16) as u16;
 
         let packet = Dns {
@@ -162,7 +164,7 @@ pub fn lookup_addr(addr: in_addr) -> Result<Vec<Vec<u8>>, c_int> {
 
     if dns_vec.len() == 4 {
         let mut timespec = timespec::default();
-        Sys::clock_gettime(time::constants::CLOCK_REALTIME, &mut timespec);
+        unsafe { Sys::clock_gettime(time::constants::CLOCK_REALTIME, &mut timespec) };
         let tid = (timespec.tv_nsec >> 16) as u16;
 
         let packet = Dns {
diff --git a/src/header/pty/redox.rs b/src/header/pty/redox.rs
index 160889fdb7d261fb44b36a70a865d81cf14f5ea0..f22dfc43edaca9b00088482283aef0b0142fd844 100644
--- a/src/header/pty/redox.rs
+++ b/src/header/pty/redox.rs
@@ -1,4 +1,5 @@
 use crate::{
+    error::ResultExt,
     header::{fcntl, unistd},
     platform::types::*,
     Pal, Sys,
@@ -10,7 +11,8 @@ pub(super) unsafe fn openpty(name: &mut [u8]) -> Result<(c_int, c_int), ()> {
         return Err(());
     }
 
-    let count = Sys::fpath(master, name);
+    // TODO: better error handling
+    let count = Sys::fpath(master, name).or_minus_one_errno();
     if count < 0 {
         unistd::close(master);
         return Err(());
diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs
index a2f48b5753c4fc9b7bc54056ee02131436ca517e..6a52cbd5b9095400e4ffbf1c4204711ea4a2894c 100644
--- a/src/header/sched/mod.rs
+++ b/src/header/sched/mod.rs
@@ -1,6 +1,7 @@
 //! sched.h implementation for Redox, following https://pubs.opengroup.org/onlinepubs/7908799/xsh/sched.h.html
 
 use crate::{
+    error::ResultExt,
     header::time::timespec,
     platform::{types::*, Pal, Sys},
 };
@@ -44,5 +45,5 @@ pub extern "C" fn sched_setscheduler(
 }
 #[no_mangle]
 pub extern "C" fn sched_yield() -> c_int {
-    Sys::sched_yield()
+    Sys::sched_yield().map(|()| 0).or_minus_one_errno()
 }
diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs
index 878bc58bba88131e307abbaf947b5ad7333e7fd0..e36a182db69fde296dfb3c048d1380ff5c9c000f 100644
--- a/src/header/stdio/mod.rs
+++ b/src/header/stdio/mod.rs
@@ -17,6 +17,7 @@ use core::{
 use crate::{
     c_str::CStr,
     c_vec::CVec,
+    error::ResultExt,
     fs::File,
     header::{
         errno::{self, STR_ERROR},
@@ -326,7 +327,8 @@ pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int {
     flockfile(stream);
 
     let mut r = stream.flush().is_err();
-    let close = Sys::close(*stream.file) < 0;
+    // TODO: better error handling
+    let close = Sys::close(*stream.file).map(|()| 0).or_minus_one_errno() == -1;
     r = r || close;
 
     if stream.flags & constants::F_PERM == 0 {
@@ -635,7 +637,7 @@ pub unsafe extern "C" fn freopen(
         let new = &mut *new; // Should be safe, new is not null
         if *new.file == *stream.file {
             new.file.fd = -1;
-        } else if Sys::dup2(*new.file, *stream.file) < 0
+        } else if Sys::dup2(*new.file, *stream.file).or_minus_one_errno() == -1
             || fcntl::fcntl(
                 *stream.file,
                 fcntl::F_SETFL,
@@ -680,7 +682,7 @@ pub unsafe fn fseek_locked(stream: &mut FILE, mut off: off_t, whence: c_int) ->
         return -1;
     }
 
-    let err = Sys::lseek(*stream.file, off, whence);
+    let err = Sys::lseek(*stream.file, off, whence).or_minus_one_errno();
     if err < 0 {
         return err as c_int;
     }
@@ -711,7 +713,7 @@ pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t {
     ftell_locked(&mut *stream)
 }
 pub unsafe extern "C" fn ftell_locked(stream: &mut FILE) -> off_t {
-    let pos = Sys::lseek(*stream.file, 0, SEEK_CUR);
+    let pos = Sys::lseek(*stream.file, 0, SEEK_CUR).or_minus_one_errno();
     if pos < 0 {
         return -1;
     }
@@ -1007,12 +1009,10 @@ pub unsafe extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int {
 #[no_mangle]
 pub unsafe extern "C" fn remove(path: *const c_char) -> c_int {
     let path = CStr::from_ptr(path);
-    let r = Sys::unlink(path);
-    if r == -errno::EISDIR {
-        Sys::rmdir(path)
-    } else {
-        r
-    }
+    Sys::unlink(path)
+        .or_else(|_err| Sys::rmdir(path))
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -1020,6 +1020,8 @@ pub unsafe extern "C" fn rename(oldpath: *const c_char, newpath: *const c_char)
     let oldpath = CStr::from_ptr(oldpath);
     let newpath = CStr::from_ptr(newpath);
     Sys::rename(oldpath, newpath)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 /// Rewind `stream` back to the beginning of it
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
index 8be117132bb175e44dd94173aa85652b084e64a1..47dd99e5eb3c8ba75925a3270441b841e33d8b8e 100644
--- a/src/header/stdlib/mod.rs
+++ b/src/header/stdlib/mod.rs
@@ -10,7 +10,7 @@ use rand_xorshift::XorShiftRng;
 
 use crate::{
     c_str::CStr,
-    error::ResultExt,
+    error::{Errno, ResultExt},
     fs::File,
     header::{
         ctype,
@@ -649,7 +649,7 @@ where
 pub unsafe extern "C" fn mktemp(name: *mut c_char) -> *mut c_char {
     if inner_mktemp(name, 0, || {
         let name = CStr::from_ptr(name);
-        if Sys::access(name, 0) != 0 && platform::ERRNO.get() == ENOENT {
+        if Sys::access(name, 0) == Err(Errno(ENOENT)) {
             Some(())
         } else {
             None
@@ -663,9 +663,11 @@ pub unsafe extern "C" fn mktemp(name: *mut c_char) -> *mut c_char {
 }
 
 fn get_nstime() -> u64 {
-    let mut ts = mem::MaybeUninit::uninit();
-    Sys::clock_gettime(CLOCK_MONOTONIC, ts.as_mut_ptr());
-    unsafe { ts.assume_init() }.tv_nsec as u64
+    unsafe {
+        let mut ts = mem::MaybeUninit::uninit();
+        Sys::clock_gettime(CLOCK_MONOTONIC, ts.as_mut_ptr());
+        ts.assume_init().tv_nsec as u64
+    }
 }
 
 #[no_mangle]
@@ -983,7 +985,8 @@ pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char
         };
 
         let len = out.len();
-        let read = Sys::fpath(*file, &mut out[..len - 1]);
+        // TODO: better error handling
+        let read = Sys::fpath(*file, &mut out[..len - 1]).or_minus_one_errno();
         if read < 0 {
             return ptr::null_mut();
         }
diff --git a/src/header/sys_resource/mod.rs b/src/header/sys_resource/mod.rs
index 2437248d3d2cab8ad853d7e6d48096ee3e9d6e3f..d8a44b968651f075ed4fb5c328e7fecc9a0326c6 100644
--- a/src/header/sys_resource/mod.rs
+++ b/src/header/sys_resource/mod.rs
@@ -2,6 +2,7 @@
 //! http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysresource.h.html
 
 use crate::{
+    error::ResultExt,
     header::sys_time::timeval,
     platform::{types::*, Pal, Sys},
 };
@@ -77,16 +78,22 @@ pub unsafe extern "C" fn getpriority(which: c_int, who: id_t) -> c_int {
 #[no_mangle]
 pub unsafe extern "C" fn setpriority(which: c_int, who: id_t, nice: c_int) -> c_int {
     Sys::setpriority(which, who, nice)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn getrlimit(resource: c_int, rlp: *mut rlimit) -> c_int {
     Sys::getrlimit(resource, rlp)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn setrlimit(resource: c_int, rlp: *const rlimit) -> c_int {
     Sys::setrlimit(resource, rlp)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
diff --git a/src/header/sys_stat/mod.rs b/src/header/sys_stat/mod.rs
index 6ec1546e6fb62bb25b85cd2e710ac7bdcab8dd85..17dfe6bcd339630422fce1e2b4cb433a548dc69a 100644
--- a/src/header/sys_stat/mod.rs
+++ b/src/header/sys_stat/mod.rs
@@ -68,12 +68,12 @@ pub struct stat {
 #[no_mangle]
 pub unsafe extern "C" fn chmod(path: *const c_char, mode: mode_t) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::chmod(path, mode)
+    Sys::chmod(path, mode).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn fchmod(fildes: c_int, mode: mode_t) -> c_int {
-    Sys::fchmod(fildes, mode)
+    Sys::fchmod(fildes, mode).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -110,19 +110,19 @@ pub unsafe extern "C" fn lstat(path: *const c_char, buf: *mut stat) -> c_int {
 #[no_mangle]
 pub unsafe extern "C" fn mkdir(path: *const c_char, mode: mode_t) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::mkdir(path, mode)
+    Sys::mkdir(path, mode).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn mkfifo(path: *const c_char, mode: mode_t) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::mkfifo(path, mode)
+    Sys::mkfifo(path, mode).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn mknod(path: *const c_char, mode: mode_t, dev: dev_t) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::mknod(path, mode, dev)
+    Sys::mknod(path, mode, dev).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -134,6 +134,8 @@ pub unsafe extern "C" fn mknodat(
 ) -> c_int {
     let path = CStr::from_ptr(path);
     Sys::mknodat(dirfd, path, mode, dev)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
diff --git a/src/header/sys_utsname/mod.rs b/src/header/sys_utsname/mod.rs
index 821868ba545bcdd122e2ac593dc1042a1834e331..a1f485b20b373be5c577a96f48d772c9131bf3d1 100644
--- a/src/header/sys_utsname/mod.rs
+++ b/src/header/sys_utsname/mod.rs
@@ -1,6 +1,9 @@
 //! sys/utsname implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html
 
-use crate::platform::{types::*, Pal, Sys};
+use crate::{
+    error::ResultExt,
+    platform::{types::*, Pal, Sys},
+};
 
 pub const UTSLENGTH: usize = 65;
 
@@ -16,5 +19,5 @@ pub struct utsname {
 
 #[no_mangle]
 pub unsafe extern "C" fn uname(uts: *mut utsname) -> c_int {
-    Sys::uname(uts)
+    Sys::uname(uts).map(|()| 0).or_minus_one_errno()
 }
diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs
index 2fa6cac5672c757b0a4d36cde3ed68885fd82caa..0ea67045389cc9a6f20a1681f75aa326dfcedf88 100644
--- a/src/header/time/mod.rs
+++ b/src/header/time/mod.rs
@@ -3,6 +3,7 @@
 use core::convert::{TryFrom, TryInto};
 
 use crate::{
+    error::ResultExt,
     header::errno::EOVERFLOW,
     platform::{self, types::*, Pal, Sys},
 };
@@ -228,7 +229,7 @@ pub unsafe extern "C" fn asctime_r(tm: *const tm, buf: *mut c_char) -> *mut c_ch
 pub extern "C" fn clock() -> clock_t {
     let mut ts = core::mem::MaybeUninit::<timespec>::uninit();
 
-    if clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts.as_mut_ptr()) != 0 {
+    if unsafe { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts.as_mut_ptr()) } != 0 {
         return -1;
     }
     let ts = unsafe { ts.assume_init() };
@@ -242,18 +243,24 @@ pub extern "C" fn clock() -> clock_t {
 }
 
 #[no_mangle]
-pub extern "C" fn clock_getres(clock_id: clockid_t, tp: *mut timespec) -> c_int {
+pub unsafe extern "C" fn clock_getres(clock_id: clockid_t, tp: *mut timespec) -> c_int {
     Sys::clock_getres(clock_id, tp)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
-pub extern "C" fn clock_gettime(clock_id: clockid_t, tp: *mut timespec) -> c_int {
+pub unsafe extern "C" fn clock_gettime(clock_id: clockid_t, tp: *mut timespec) -> c_int {
     Sys::clock_gettime(clock_id, tp)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
-pub extern "C" fn clock_settime(clock_id: clockid_t, tp: *const timespec) -> c_int {
+pub unsafe extern "C" fn clock_settime(clock_id: clockid_t, tp: *const timespec) -> c_int {
     Sys::clock_settime(clock_id, tp)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs
index 981fea243aa2d79cdba5b2c4665d2ef6a1d0c003..03ef809cbbed04d34049495d7c1763c38533a963 100644
--- a/src/header/unistd/mod.rs
+++ b/src/header/unistd/mod.rs
@@ -72,7 +72,7 @@ pub extern "C" fn _exit(status: c_int) {
 #[no_mangle]
 pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::access(path, mode)
+    Sys::access(path, mode).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -98,7 +98,7 @@ pub extern "C" fn alarm(seconds: c_uint) -> c_uint {
 #[no_mangle]
 pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::chdir(path)
+    Sys::chdir(path).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -121,11 +121,13 @@ pub unsafe extern "C" fn set_default_scheme(scheme: *const c_char) -> c_int {
 pub unsafe extern "C" fn chown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int {
     let path = CStr::from_ptr(path);
     Sys::chown(path, owner, group)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn close(fildes: c_int) -> c_int {
-    Sys::close(fildes)
+    Sys::close(fildes).map(|()| 0).or_minus_one_errno()
 }
 
 // #[no_mangle]
@@ -142,7 +144,7 @@ pub unsafe extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut
 #[no_mangle]
 pub extern "C" fn daemon(nochdir: c_int, noclose: c_int) -> c_int {
     if nochdir == 0 {
-        if Sys::chdir(c_str!("/")) < 0 {
+        if Sys::chdir(c_str!("/")).map(|()| 0).or_minus_one_errno() < 0 {
             return -1;
         }
     }
@@ -176,12 +178,12 @@ pub extern "C" fn daemon(nochdir: c_int, noclose: c_int) -> c_int {
 
 #[no_mangle]
 pub extern "C" fn dup(fildes: c_int) -> c_int {
-    Sys::dup(fildes)
+    Sys::dup(fildes).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int {
-    Sys::dup2(fildes, fildes2)
+    Sys::dup2(fildes, fildes2).or_minus_one_errno()
 }
 
 // #[no_mangle]
@@ -327,16 +329,18 @@ pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -
 #[no_mangle]
 pub extern "C" fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int {
     Sys::fchown(fildes, owner, group)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn fchdir(fildes: c_int) -> c_int {
-    Sys::fchdir(fildes)
+    Sys::fchdir(fildes).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn fdatasync(fildes: c_int) -> c_int {
-    Sys::fdatasync(fildes)
+    Sys::fdatasync(fildes).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -449,7 +453,10 @@ pub extern "C" fn gethostid() -> c_long {
 #[no_mangle]
 pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int {
     let mut uts = mem::MaybeUninit::<sys_utsname::utsname>::uninit();
-    let err = Sys::uname(uts.as_mut_ptr());
+    // TODO
+    let err = Sys::uname(uts.as_mut_ptr())
+        .map(|()| 0)
+        .or_minus_one_errno();
     if err < 0 {
         mem::forget(uts);
         return err;
@@ -546,13 +553,15 @@ pub extern "C" fn isatty(fd: c_int) -> c_int {
 pub unsafe extern "C" fn lchown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int {
     let path = CStr::from_ptr(path);
     Sys::lchown(path, owner, group)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int {
     let path1 = CStr::from_ptr(path1);
     let path2 = CStr::from_ptr(path2);
-    Sys::link(path1, path2)
+    Sys::link(path1, path2).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -596,7 +605,7 @@ pub unsafe extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c
 
 #[no_mangle]
 pub extern "C" fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t {
-    Sys::lseek(fildes, offset, whence)
+    Sys::lseek(fildes, offset, whence).or_minus_one_errno()
 }
 
 // #[no_mangle]
@@ -617,6 +626,8 @@ pub unsafe extern "C" fn pipe(fildes: *mut c_int) -> c_int {
 #[no_mangle]
 pub unsafe extern "C" fn pipe2(fildes: *mut c_int, flags: c_int) -> c_int {
     Sys::pipe2(slice::from_raw_parts_mut(fildes, 2), flags)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -688,28 +699,30 @@ pub unsafe extern "C" fn readlink(
 ) -> ssize_t {
     let path = CStr::from_ptr(path);
     let buf = slice::from_raw_parts_mut(buf as *mut u8, bufsize as usize);
-    Sys::readlink(path, buf)
+    Sys::readlink(path, buf).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::rmdir(path)
+    Sys::rmdir(path).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn setgid(gid: gid_t) -> c_int {
     Sys::setresgid(gid, gid, -1)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn setgroups(size: size_t, list: *const gid_t) -> c_int {
-    Sys::setgroups(size, list)
+    Sys::setgroups(size, list).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
-    Sys::setpgid(pid, pgid)
+    Sys::setpgid(pid, pgid).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -720,29 +733,39 @@ pub extern "C" fn setpgrp() -> pid_t {
 #[no_mangle]
 pub extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
     Sys::setresgid(rgid, egid, -1)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 #[no_mangle]
 pub extern "C" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> c_int {
     Sys::setresgid(rgid, egid, sgid)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
     Sys::setresuid(ruid, euid, -1)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn setsid() -> pid_t {
-    Sys::setsid()
+    Sys::setsid().map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
 pub extern "C" fn setuid(uid: uid_t) -> c_int {
     Sys::setresuid(uid, uid, -1)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 #[no_mangle]
 pub extern "C" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> c_int {
     Sys::setresuid(ruid, euid, suid)
+        .map(|()| 0)
+        .or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -776,7 +799,7 @@ pub extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) {
 pub unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int {
     let path1 = CStr::from_ptr(path1);
     let path2 = CStr::from_ptr(path2);
-    Sys::symlink(path1, path2)
+    Sys::symlink(path1, path2).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
@@ -834,7 +857,7 @@ pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t)
         return errno::ERANGE;
     }
 
-    let len = Sys::fpath(fildes, &mut name[..namesize - 1]);
+    let len = Sys::fpath(fildes, &mut name[..namesize - 1]).or_minus_one_errno();
     if len < 0 {
         return -platform::ERRNO.get();
     }
@@ -869,7 +892,7 @@ pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t
 #[no_mangle]
 pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
     let path = CStr::from_ptr(path);
-    Sys::unlink(path)
+    Sys::unlink(path).map(|()| 0).or_minus_one_errno()
 }
 
 #[no_mangle]
diff --git a/src/io/error.rs b/src/io/error.rs
index e512d94b1791b5bc881ceea6522991c21dcd6c68..5c5c582eedd3263c500afbfbdc865c47d236be55 100644
--- a/src/io/error.rs
+++ b/src/io/error.rs
@@ -11,6 +11,8 @@
 use alloc::{boxed::Box, string::String};
 use core::{fmt, result, str};
 
+use crate::platform::types::c_int;
+
 /// A specialized [`Result`](../result/enum.Result.html) type for I/O
 /// operations.
 ///
@@ -62,6 +64,17 @@ pub struct Error {
     repr: Repr,
 }
 
+// TODO?
+impl Error {
+    pub fn raw_os_error(&self) -> Option<c_int> {
+        if let Repr::Os(os) = self.repr {
+            Some(os)
+        } else {
+            None
+        }
+    }
+}
+
 impl fmt::Debug for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Debug::fmt(&self.repr, f)
diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs
index b258b52da5e586d0a943cfa960592509e2ea7c46..3258a900a205b3bcd7a1cd3286b5e8e9340d4158 100644
--- a/src/platform/linux/mod.rs
+++ b/src/platform/linux/mod.rs
@@ -17,7 +17,7 @@ use crate::{
 };
 // use header::sys_times::tms;
 use crate::{
-    error::Errno,
+    error::{Errno, Result},
     header::{sys_utsname::utsname, time::timespec},
 };
 
@@ -57,7 +57,7 @@ struct linux_statfs {
 // TODO
 const ERRNO_MAX: usize = 4095;
 
-pub fn e_raw(sys: usize) -> Result<usize, Errno> {
+pub fn e_raw(sys: usize) -> Result<usize> {
     if sys > ERRNO_MAX.wrapping_neg() {
         Err(Errno(sys.wrapping_neg() as _))
     } else {
@@ -88,28 +88,27 @@ impl Sys {
 }
 
 impl Pal for Sys {
-    fn access(path: CStr, mode: c_int) -> c_int {
-        e(unsafe { syscall!(ACCESS, path.as_ptr(), mode) }) as c_int
+    fn access(path: CStr, mode: c_int) -> Result<()> {
+        e_raw(unsafe { syscall!(ACCESS, path.as_ptr(), mode) }).map(|_| ())
     }
 
-    fn brk(addr: *mut c_void) -> *mut c_void {
+    unsafe fn brk(addr: *mut c_void) -> *mut c_void {
         unsafe { syscall!(BRK, addr) as *mut c_void }
     }
 
-    fn chdir(path: CStr) -> c_int {
-        e(unsafe { syscall!(CHDIR, path.as_ptr()) }) as c_int
+    fn chdir(path: CStr) -> Result<()> {
+        e_raw(unsafe { syscall!(CHDIR, path.as_ptr()) }).map(|_| ())
     }
-
-    fn set_default_scheme(scheme: CStr) -> Result<(), Errno> {
+    fn set_default_scheme(scheme: CStr) -> Result<()> {
         Err(Errno(EOPNOTSUPP))
     }
 
-    fn chmod(path: CStr, mode: mode_t) -> c_int {
-        e(unsafe { syscall!(FCHMODAT, AT_FDCWD, path.as_ptr(), mode, 0) }) as c_int
+    fn chmod(path: CStr, mode: mode_t) -> Result<()> {
+        e_raw(unsafe { syscall!(FCHMODAT, AT_FDCWD, path.as_ptr(), mode, 0) }).map(|_| ())
     }
 
-    fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
-        e(unsafe {
+    fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> {
+        e_raw(unsafe {
             syscall!(
                 FCHOWNAT,
                 AT_FDCWD,
@@ -117,31 +116,32 @@ impl Pal for Sys {
                 owner as u32,
                 group as u32
             )
-        }) as c_int
+        })
+        .map(|_| ())
     }
 
-    fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> c_int {
-        e(unsafe { syscall!(CLOCK_GETRES, clk_id, tp) }) as c_int
+    unsafe fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
+        e_raw(syscall!(CLOCK_GETRES, clk_id, tp)).map(|_| ())
     }
 
-    fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> c_int {
-        e(unsafe { syscall!(CLOCK_GETTIME, clk_id, tp) }) as c_int
+    unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
+        e_raw(syscall!(CLOCK_GETTIME, clk_id, tp)).map(|_| ())
     }
 
-    fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> c_int {
-        e(unsafe { syscall!(CLOCK_SETTIME, clk_id, tp) }) as c_int
+    unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()> {
+        e_raw(syscall!(CLOCK_SETTIME, clk_id, tp)).map(|_| ())
     }
 
-    fn close(fildes: c_int) -> c_int {
-        e(unsafe { syscall!(CLOSE, fildes) }) as c_int
+    fn close(fildes: c_int) -> Result<()> {
+        e_raw(unsafe { syscall!(CLOSE, fildes) }).map(|_| ())
     }
 
-    fn dup(fildes: c_int) -> c_int {
-        e(unsafe { syscall!(DUP, fildes) }) as c_int
+    fn dup(fildes: c_int) -> Result<c_int> {
+        e_raw(unsafe { syscall!(DUP, fildes) }).map(|f| f as c_int)
     }
 
-    fn dup2(fildes: c_int, fildes2: c_int) -> c_int {
-        e(unsafe { syscall!(DUP3, fildes, fildes2, 0) }) as c_int
+    fn dup2(fildes: c_int, fildes2: c_int) -> Result<c_int> {
+        e_raw(unsafe { syscall!(DUP3, fildes, fildes2, 0) }).map(|f| f as c_int)
     }
 
     unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int {
@@ -162,20 +162,20 @@ impl Pal for Sys {
         Self::exit(0)
     }
 
-    fn fchdir(fildes: c_int) -> c_int {
-        e(unsafe { syscall!(FCHDIR, fildes) }) as c_int
+    fn fchdir(fildes: c_int) -> Result<()> {
+        e_raw(unsafe { syscall!(FCHDIR, fildes) }).map(|_| ())
     }
 
-    fn fchmod(fildes: c_int, mode: mode_t) -> c_int {
-        e(unsafe { syscall!(FCHMOD, fildes, mode) }) as c_int
+    fn fchmod(fildes: c_int, mode: mode_t) -> Result<()> {
+        e_raw(unsafe { syscall!(FCHMOD, fildes, mode) }).map(|_| ())
     }
 
-    fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int {
-        e(unsafe { syscall!(FCHOWN, fildes, owner, group) }) as c_int
+    fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()> {
+        e_raw(unsafe { syscall!(FCHOWN, fildes, owner, group) }).map(|_| ())
     }
 
-    fn fdatasync(fildes: c_int) -> c_int {
-        e(unsafe { syscall!(FDATASYNC, fildes) }) as c_int
+    fn fdatasync(fildes: c_int) -> Result<()> {
+        e_raw(unsafe { syscall!(FDATASYNC, fildes) }).map(|_| ())
     }
 
     fn flock(fd: c_int, operation: c_int) -> c_int {
@@ -224,7 +224,7 @@ impl Pal for Sys {
         e(unsafe { syscall!(CLONE, SIGCHLD, 0, 0, 0, 0) }) as pid_t
     }
 
-    fn fpath(fildes: c_int, out: &mut [u8]) -> ssize_t {
+    fn fpath(fildes: c_int, out: &mut [u8]) -> Result<ssize_t> {
         let mut proc_path = b"/proc/self/fd/".to_vec();
         write!(proc_path, "{}", fildes).unwrap();
         proc_path.push(0);
@@ -232,20 +232,16 @@ impl Pal for Sys {
         Self::readlink(CStr::from_bytes_with_nul(&proc_path).unwrap(), out)
     }
 
-    fn fsync(fildes: c_int) -> Result<(), Errno> {
+    fn fsync(fildes: c_int) -> Result<()> {
         e_raw(unsafe { syscall!(FSYNC, fildes) }).map(|_| ())
     }
 
-    fn ftruncate(fildes: c_int, length: off_t) -> Result<(), Errno> {
+    fn ftruncate(fildes: c_int, length: off_t) -> Result<()> {
         e_raw(unsafe { syscall!(FTRUNCATE, fildes, length) }).map(|_| ())
     }
 
     #[inline]
-    unsafe fn futex_wait(
-        addr: *mut u32,
-        val: u32,
-        deadline: Option<&timespec>,
-    ) -> Result<(), Errno> {
+    unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<&timespec>) -> Result<()> {
         let deadline = deadline.map_or(0, |d| d as *const _ as usize);
         e_raw(unsafe {
             syscall!(
@@ -260,7 +256,7 @@ impl Pal for Sys {
         .map(|_| ())
     }
     #[inline]
-    unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32, Errno> {
+    unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32> {
         e_raw(unsafe {
             syscall!(FUTEX, addr, 1 /* FUTEX_WAKE */, num)
         })
@@ -283,10 +279,10 @@ impl Pal for Sys {
         }
     }
 
-    fn getdents(fd: c_int, buf: &mut [u8], _off: u64) -> Result<usize, Errno> {
+    fn getdents(fd: c_int, buf: &mut [u8], _off: u64) -> Result<usize> {
         e_raw(unsafe { syscall!(GETDENTS64, fd, buf.as_mut_ptr(), buf.len()) })
     }
-    fn dir_seek(fd: c_int, off: u64) -> Result<(), Errno> {
+    fn dir_seek(fd: c_int, off: u64) -> Result<()> {
         e_raw(unsafe { syscall!(LSEEK, fd, off, SEEK_SET) })?;
         Ok(())
     }
@@ -335,12 +331,12 @@ impl Pal for Sys {
         e(unsafe { syscall!(GETRANDOM, buf.as_mut_ptr(), buf.len(), flags) }) as ssize_t
     }
 
-    unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> c_int {
-        e(syscall!(GETRLIMIT, resource, rlim)) as c_int
+    unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> Result<()> {
+        e_raw(syscall!(GETRLIMIT, resource, rlim)).map(|_| ())
     }
 
-    unsafe fn setrlimit(resource: c_int, rlimit: *const rlimit) -> c_int {
-        e(syscall!(SETRLIMIT, resource, rlimit)) as c_int
+    unsafe fn setrlimit(resource: c_int, rlimit: *const rlimit) -> Result<()> {
+        e_raw(syscall!(SETRLIMIT, resource, rlimit)).map(|_| ())
     }
 
     fn getrusage(who: c_int, r_usage: &mut rusage) -> c_int {
@@ -363,12 +359,12 @@ impl Pal for Sys {
         e(unsafe { syscall!(GETUID) }) as uid_t
     }
 
-    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
-        e(unsafe { syscall!(LCHOWN, path.as_ptr(), owner, group) }) as c_int
+    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> {
+        e_raw(unsafe { syscall!(LCHOWN, path.as_ptr(), owner, group) }).map(|_| ())
     }
 
-    fn link(path1: CStr, path2: CStr) -> c_int {
-        e(unsafe {
+    fn link(path1: CStr, path2: CStr) -> Result<()> {
+        e_raw(unsafe {
             syscall!(
                 LINKAT,
                 AT_FDCWD,
@@ -377,33 +373,34 @@ impl Pal for Sys {
                 path2.as_ptr(),
                 0
             )
-        }) as c_int
+        })
+        .map(|_| ())
     }
 
-    fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t {
-        e(unsafe { syscall!(LSEEK, fildes, offset, whence) }) as off_t
+    fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result<off_t> {
+        e_raw(unsafe { syscall!(LSEEK, fildes, offset, whence) }).map(|o| o as off_t)
     }
 
-    fn mkdir(path: CStr, mode: mode_t) -> c_int {
-        e(unsafe { syscall!(MKDIRAT, AT_FDCWD, path.as_ptr(), mode) }) as c_int
+    fn mkdir(path: CStr, mode: mode_t) -> Result<()> {
+        e_raw(unsafe { syscall!(MKDIRAT, AT_FDCWD, path.as_ptr(), mode) }).map(|_| ())
     }
 
-    fn mknodat(dir_fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> c_int {
+    fn mknodat(dir_fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> Result<()> {
         // Note: dev_t is c_long (i64) and __kernel_dev_t is u32; So we need to cast it
         //       and check for overflow
         let k_dev: c_uint = dev as c_uint;
         if k_dev as dev_t != dev {
-            return e(EINVAL as usize) as c_int;
+            return Err(Errno(EINVAL));
         }
 
-        e(unsafe { syscall!(MKNODAT, dir_fildes, path.as_ptr(), mode, k_dev) }) as c_int
+        e_raw(unsafe { syscall!(MKNODAT, dir_fildes, path.as_ptr(), mode, k_dev) }).map(|_| ())
     }
 
-    fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> c_int {
+    fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> Result<()> {
         Sys::mknodat(AT_FDCWD, path, mode, dev)
     }
 
-    fn mkfifo(path: CStr, mode: mode_t) -> c_int {
+    fn mkfifo(path: CStr, mode: mode_t) -> Result<()> {
         Sys::mknod(path, mode | S_IFIFO, 0)
     }
 
@@ -464,17 +461,17 @@ impl Pal for Sys {
         e(unsafe { syscall!(NANOSLEEP, rqtp, rmtp) }) as c_int
     }
 
-    fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int, Errno> {
+    fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int> {
         e_raw(unsafe { syscall!(OPENAT, AT_FDCWD, path.as_ptr(), oflag, mode) })
             .map(|fd| fd as c_int)
     }
 
-    fn pipe2(fildes: &mut [c_int], flags: c_int) -> c_int {
-        e(unsafe { syscall!(PIPE2, fildes.as_mut_ptr(), flags) }) as c_int
+    fn pipe2(fildes: &mut [c_int], flags: c_int) -> Result<()> {
+        e_raw(unsafe { syscall!(PIPE2, fildes.as_mut_ptr(), flags) }).map(|_| ())
     }
 
     #[cfg(target_arch = "x86_64")]
-    unsafe fn rlct_clone(stack: *mut usize) -> Result<crate::pthread::OsTid, Errno> {
+    unsafe fn rlct_clone(stack: *mut usize) -> Result<crate::pthread::OsTid> {
         let flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD;
         let pid;
         asm!("
@@ -527,7 +524,7 @@ impl Pal for Sys {
 
         Ok(crate::pthread::OsTid { thread_id: tid })
     }
-    unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<(), Errno> {
+    unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<()> {
         let tgid = Self::getpid();
         e_raw(unsafe { syscall!(TGKILL, tgid, os_tid.thread_id, signal) }).map(|_| ())
     }
@@ -537,18 +534,18 @@ impl Pal for Sys {
         }
     }
 
-    fn read(fildes: c_int, buf: &mut [u8]) -> Result<ssize_t, Errno> {
+    fn read(fildes: c_int, buf: &mut [u8]) -> Result<ssize_t> {
         Ok(e_raw(unsafe { syscall!(READ, fildes, buf.as_mut_ptr(), buf.len()) })? as ssize_t)
     }
-    fn pread(fildes: c_int, buf: &mut [u8], off: off_t) -> Result<ssize_t, Errno> {
+    fn pread(fildes: c_int, buf: &mut [u8], off: off_t) -> Result<ssize_t> {
         Ok(
             e_raw(unsafe { syscall!(PREAD64, fildes, buf.as_mut_ptr(), buf.len(), off) })?
                 as ssize_t,
         )
     }
 
-    fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t {
-        e(unsafe {
+    fn readlink(pathname: CStr, out: &mut [u8]) -> Result<ssize_t> {
+        e_raw(unsafe {
             syscall!(
                 READLINKAT,
                 AT_FDCWD,
@@ -556,73 +553,75 @@ impl Pal for Sys {
                 out.as_mut_ptr(),
                 out.len()
             )
-        }) as ssize_t
+        })
+        .map(|b| b as ssize_t)
     }
 
-    fn rename(old: CStr, new: CStr) -> c_int {
-        e(unsafe { syscall!(RENAMEAT, AT_FDCWD, old.as_ptr(), AT_FDCWD, new.as_ptr()) }) as c_int
+    fn rename(old: CStr, new: CStr) -> Result<()> {
+        e_raw(unsafe { syscall!(RENAMEAT, AT_FDCWD, old.as_ptr(), AT_FDCWD, new.as_ptr()) })
+            .map(|_| ())
     }
 
-    fn rmdir(path: CStr) -> c_int {
-        e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), AT_REMOVEDIR) }) as c_int
+    fn rmdir(path: CStr) -> Result<()> {
+        e_raw(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), AT_REMOVEDIR) }).map(|_| ())
     }
 
-    fn sched_yield() -> c_int {
-        e(unsafe { syscall!(SCHED_YIELD) }) as c_int
+    fn sched_yield() -> Result<()> {
+        e_raw(unsafe { syscall!(SCHED_YIELD) }).map(|_| ())
     }
 
-    unsafe fn setgroups(size: size_t, list: *const gid_t) -> c_int {
-        e(unsafe { syscall!(SETGROUPS, size, list) }) as c_int
+    unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()> {
+        e_raw(unsafe { syscall!(SETGROUPS, size, list) }).map(|_| ())
     }
 
-    fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
-        e(unsafe { syscall!(SETPGID, pid, pgid) }) as c_int
+    fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> {
+        e_raw(unsafe { syscall!(SETPGID, pid, pgid) }).map(|_| ())
     }
 
-    fn setpriority(which: c_int, who: id_t, prio: c_int) -> c_int {
-        e(unsafe { syscall!(SETPRIORITY, which, who, prio) }) as c_int
+    fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()> {
+        e_raw(unsafe { syscall!(SETPRIORITY, which, who, prio) }).map(|_| ())
     }
 
-    fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> c_int {
-        e(unsafe { syscall!(SETRESGID, rgid, egid, sgid) }) as c_int
+    fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> {
+        e_raw(unsafe { syscall!(SETRESGID, rgid, egid, sgid) }).map(|_| ())
     }
 
-    fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> c_int {
-        e(unsafe { syscall!(SETRESUID, ruid, euid, suid) }) as c_int
+    fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> {
+        e_raw(unsafe { syscall!(SETRESUID, ruid, euid, suid) }).map(|_| ())
     }
 
-    fn setsid() -> c_int {
-        e(unsafe { syscall!(SETSID) }) as c_int
+    fn setsid() -> Result<()> {
+        e_raw(unsafe { syscall!(SETSID) }).map(|_| ())
     }
 
-    fn symlink(path1: CStr, path2: CStr) -> c_int {
-        e(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), AT_FDCWD, path2.as_ptr()) }) as c_int
+    fn symlink(path1: CStr, path2: CStr) -> Result<()> {
+        e_raw(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), AT_FDCWD, path2.as_ptr()) }).map(|_| ())
     }
 
-    fn sync() -> c_int {
-        e(unsafe { syscall!(SYNC) }) as c_int
+    fn sync() -> Result<()> {
+        e_raw(unsafe { syscall!(SYNC) }).map(|_| ())
     }
 
     fn umask(mask: mode_t) -> mode_t {
         unsafe { syscall!(UMASK, mask) as mode_t }
     }
 
-    fn uname(utsname: *mut utsname) -> c_int {
-        e(unsafe { syscall!(UNAME, utsname, 0) }) as c_int
+    unsafe fn uname(utsname: *mut utsname) -> Result<()> {
+        e_raw(syscall!(UNAME, utsname, 0)).map(|_| ())
     }
 
-    fn unlink(path: CStr) -> c_int {
-        e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), 0) }) as c_int
+    fn unlink(path: CStr) -> Result<()> {
+        e_raw(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), 0) }).map(|_| ())
     }
 
     fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t {
         e(unsafe { syscall!(WAIT4, pid, stat_loc, options, 0) }) as pid_t
     }
 
-    fn write(fildes: c_int, buf: &[u8]) -> Result<ssize_t, Errno> {
+    fn write(fildes: c_int, buf: &[u8]) -> Result<ssize_t> {
         Ok(e_raw(unsafe { syscall!(WRITE, fildes, buf.as_ptr(), buf.len()) })? as ssize_t)
     }
-    fn pwrite(fildes: c_int, buf: &[u8], off: off_t) -> Result<ssize_t, Errno> {
+    fn pwrite(fildes: c_int, buf: &[u8], off: off_t) -> Result<ssize_t> {
         Ok(e_raw(unsafe { syscall!(PWRITE64, fildes, buf.as_ptr(), buf.len(), off) })? as ssize_t)
     }
 
diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs
index 09dea6cba96fb5e7e78b5f7a52803b14e32b329b..e8716defdea9e06d38a38c1147286add0b8ee2c7 100644
--- a/src/platform/pal/mod.rs
+++ b/src/platform/pal/mod.rs
@@ -1,7 +1,7 @@
 use super::types::*;
 use crate::{
     c_str::CStr,
-    error::Errno,
+    error::{Errno, Result},
     header::{
         sys_resource::{rlimit, rusage},
         sys_stat::stat,
@@ -26,29 +26,29 @@ pub use self::socket::PalSocket;
 mod socket;
 
 pub trait Pal {
-    fn access(path: CStr, mode: c_int) -> c_int;
+    fn access(path: CStr, mode: c_int) -> Result<()>;
 
-    fn brk(addr: *mut c_void) -> *mut c_void;
+    unsafe fn brk(addr: *mut c_void) -> *mut c_void;
 
-    fn chdir(path: CStr) -> c_int;
+    fn chdir(path: CStr) -> Result<()>;
 
     fn set_default_scheme(scheme: CStr) -> Result<(), Errno>;
 
-    fn chmod(path: CStr, mode: mode_t) -> c_int;
+    fn chmod(path: CStr, mode: mode_t) -> Result<()>;
 
-    fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int;
+    fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()>;
 
-    fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> c_int;
+    unsafe fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> Result<()>;
 
-    fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> c_int;
+    unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> Result<()>;
 
-    fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> c_int;
+    unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()>;
 
-    fn close(fildes: c_int) -> c_int;
+    fn close(fildes: c_int) -> Result<()>;
 
-    fn dup(fildes: c_int) -> c_int;
+    fn dup(fildes: c_int) -> Result<c_int>;
 
-    fn dup2(fildes: c_int, fildes2: c_int) -> c_int;
+    fn dup2(fildes: c_int, fildes2: c_int) -> Result<c_int>;
 
     unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int;
     unsafe fn fexecve(fildes: c_int, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int;
@@ -57,13 +57,13 @@ pub trait Pal {
 
     unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> !;
 
-    fn fchdir(fildes: c_int) -> c_int;
+    fn fchdir(fildes: c_int) -> Result<()>;
 
-    fn fchmod(fildes: c_int, mode: mode_t) -> c_int;
+    fn fchmod(fildes: c_int, mode: mode_t) -> Result<()>;
 
-    fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int;
+    fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()>;
 
-    fn fdatasync(fildes: c_int) -> c_int;
+    fn fdatasync(fildes: c_int) -> Result<()>;
 
     fn flock(fd: c_int, operation: c_int) -> c_int;
 
@@ -75,18 +75,14 @@ pub trait Pal {
 
     fn fork() -> pid_t;
 
-    fn fpath(fildes: c_int, out: &mut [u8]) -> ssize_t;
+    fn fpath(fildes: c_int, out: &mut [u8]) -> Result<ssize_t>;
 
-    fn fsync(fildes: c_int) -> Result<(), Errno>;
+    fn fsync(fildes: c_int) -> Result<()>;
 
-    fn ftruncate(fildes: c_int, length: off_t) -> Result<(), Errno>;
+    fn ftruncate(fildes: c_int, length: off_t) -> Result<()>;
 
-    unsafe fn futex_wait(
-        addr: *mut u32,
-        val: u32,
-        deadline: Option<&timespec>,
-    ) -> Result<(), Errno>;
-    unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32, Errno>;
+    unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<&timespec>) -> Result<()>;
+    unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32>;
 
     fn futimens(fd: c_int, times: *const timespec) -> c_int;
 
@@ -94,8 +90,8 @@ pub trait Pal {
 
     fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
 
-    fn getdents(fd: c_int, buf: &mut [u8], opaque_offset: u64) -> Result<usize, Errno>;
-    fn dir_seek(fd: c_int, opaque_offset: u64) -> Result<(), Errno>;
+    fn getdents(fd: c_int, buf: &mut [u8], opaque_offset: u64) -> Result<usize>;
+    fn dir_seek(fd: c_int, opaque_offset: u64) -> Result<()>;
 
     // SAFETY: This_dent must satisfy platform-specific size and alignment constraints. On Linux,
     // this means the buffer came from a valid getdents64 invocation, whereas on Redox, every
@@ -125,9 +121,9 @@ pub trait Pal {
 
     fn getrandom(buf: &mut [u8], flags: c_uint) -> ssize_t;
 
-    unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> c_int;
+    unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> Result<()>;
 
-    unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> c_int;
+    unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()>;
 
     fn getrusage(who: c_int, r_usage: &mut rusage) -> c_int;
 
@@ -139,19 +135,19 @@ pub trait Pal {
 
     fn getuid() -> uid_t;
 
-    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int;
+    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()>;
 
-    fn link(path1: CStr, path2: CStr) -> c_int;
+    fn link(path1: CStr, path2: CStr) -> Result<()>;
 
-    fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t;
+    fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result<off_t>;
 
-    fn mkdir(path: CStr, mode: mode_t) -> c_int;
+    fn mkdir(path: CStr, mode: mode_t) -> Result<()>;
 
-    fn mkfifo(path: CStr, mode: mode_t) -> c_int;
+    fn mkfifo(path: CStr, mode: mode_t) -> Result<()>;
 
-    fn mknodat(fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> c_int;
+    fn mknodat(fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> Result<()>;
 
-    fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> c_int;
+    fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> Result<()>;
 
     unsafe fn mlock(addr: *const c_void, len: usize) -> c_int;
 
@@ -188,52 +184,52 @@ pub trait Pal {
 
     fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int;
 
-    fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int, Errno>;
+    fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int>;
 
-    fn pipe2(fildes: &mut [c_int], flags: c_int) -> c_int;
+    fn pipe2(fildes: &mut [c_int], flags: c_int) -> Result<()>;
 
     unsafe fn rlct_clone(stack: *mut usize) -> Result<pthread::OsTid, Errno>;
-    unsafe fn rlct_kill(os_tid: pthread::OsTid, signal: usize) -> Result<(), Errno>;
+    unsafe fn rlct_kill(os_tid: pthread::OsTid, signal: usize) -> Result<()>;
 
     fn current_os_tid() -> pthread::OsTid;
 
-    fn read(fildes: c_int, buf: &mut [u8]) -> Result<ssize_t, Errno>;
-    fn pread(fildes: c_int, buf: &mut [u8], offset: off_t) -> Result<ssize_t, Errno>;
+    fn read(fildes: c_int, buf: &mut [u8]) -> Result<ssize_t>;
+    fn pread(fildes: c_int, buf: &mut [u8], offset: off_t) -> Result<ssize_t>;
 
-    fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t;
+    fn readlink(pathname: CStr, out: &mut [u8]) -> Result<ssize_t>;
 
-    fn rename(old: CStr, new: CStr) -> c_int;
+    fn rename(old: CStr, new: CStr) -> Result<()>;
 
-    fn rmdir(path: CStr) -> c_int;
+    fn rmdir(path: CStr) -> Result<()>;
 
-    fn sched_yield() -> c_int;
+    fn sched_yield() -> Result<()>;
 
-    unsafe fn setgroups(size: size_t, list: *const gid_t) -> c_int;
+    unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()>;
 
-    fn setpgid(pid: pid_t, pgid: pid_t) -> c_int;
+    fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()>;
 
-    fn setpriority(which: c_int, who: id_t, prio: c_int) -> c_int;
+    fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()>;
 
-    fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> c_int;
+    fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()>;
 
-    fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> c_int;
+    fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()>;
 
-    fn setsid() -> c_int;
+    fn setsid() -> Result<()>;
 
-    fn symlink(path1: CStr, path2: CStr) -> c_int;
+    fn symlink(path1: CStr, path2: CStr) -> Result<()>;
 
-    fn sync() -> c_int;
+    fn sync() -> Result<()>;
 
     fn umask(mask: mode_t) -> mode_t;
 
-    fn uname(utsname: *mut utsname) -> c_int;
+    unsafe fn uname(utsname: *mut utsname) -> Result<()>;
 
-    fn unlink(path: CStr) -> c_int;
+    fn unlink(path: CStr) -> Result<()>;
 
     fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t;
 
-    fn write(fildes: c_int, buf: &[u8]) -> Result<ssize_t, Errno>;
-    fn pwrite(fildes: c_int, buf: &[u8], offset: off_t) -> Result<ssize_t, Errno>;
+    fn write(fildes: c_int, buf: &[u8]) -> Result<ssize_t>;
+    fn pwrite(fildes: c_int, buf: &[u8], offset: off_t) -> Result<ssize_t>;
 
     fn verify() -> bool;
 }
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 62e90f61cbcae6a7694909071dabda04d24d85f2..62cfab1ac94e36a6d9d5a3dbecbab982109e24b3 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -8,12 +8,12 @@ use syscall::{
     self,
     data::{Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec},
     dirent::{DirentHeader, DirentKind},
-    Error, PtraceEvent, Result, EMFILE, MODE_PERM,
+    Error, PtraceEvent, EMFILE, MODE_PERM,
 };
 
 use crate::{
     c_str::{CStr, CString},
-    error::{self, Errno, ResultExt},
+    error::{self, Errno, Result, ResultExt},
     fs::File,
     header::{
         dirent::dirent,
@@ -74,7 +74,7 @@ macro_rules! path_from_c_str {
 
 use self::{exec::Executable, path::canonicalize};
 
-pub fn e(sys: Result<usize>) -> usize {
+pub fn e(sys: syscall::error::Result<usize>) -> usize {
     match sys {
         Ok(ok) => ok,
         Err(err) => {
@@ -87,30 +87,19 @@ pub fn e(sys: Result<usize>) -> usize {
 pub struct Sys;
 
 impl Pal for Sys {
-    fn access(path: CStr, mode: c_int) -> c_int {
-        let fd = match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
-            Ok(fd) => fd,
-            Err(_) => return -1,
-        };
+    fn access(path: CStr, mode: c_int) -> Result<()> {
+        let fd = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
 
         if mode == F_OK {
-            return 0;
+            return Ok(());
         }
 
         let mut stat = syscall::Stat::default();
 
-        if e(syscall::fstat(*fd as usize, &mut stat)) == !0 {
-            return -1;
-        }
+        syscall::fstat(*fd as usize, &mut stat)?;
 
-        let uid = e(syscall::getuid());
-        if uid == !0 {
-            return -1;
-        }
-        let gid = e(syscall::getgid());
-        if gid == !0 {
-            return -1;
-        }
+        let uid = syscall::getuid()?;
+        let gid = syscall::getgid()?;
 
         let perms = if stat.st_uid as usize == uid {
             stat.st_mode >> (3 * 2 & 0o7)
@@ -123,111 +112,101 @@ impl Pal for Sys {
             || (mode & W_OK == W_OK && perms & 0o2 != 0o2)
             || (mode & X_OK == X_OK && perms & 0o1 != 0o1)
         {
-            ERRNO.set(EINVAL);
-            return -1;
+            return Err(Errno(EINVAL));
         }
 
-        0
+        Ok(())
     }
 
-    fn brk(addr: *mut c_void) -> *mut c_void {
-        unsafe {
-            // On first invocation, allocate a buffer for brk
-            if BRK_CUR.is_null() {
-                // 4 megabytes of RAM ought to be enough for anybody
-                const BRK_MAX_SIZE: usize = 4 * 1024 * 1024;
-
-                let allocated = Self::mmap(
-                    ptr::null_mut(),
-                    BRK_MAX_SIZE,
-                    PROT_READ | PROT_WRITE,
-                    MAP_ANONYMOUS,
-                    0,
-                    0,
-                );
-                if allocated == !0 as *mut c_void
-                /* MAP_FAILED */
-                {
-                    return !0 as *mut c_void;
-                }
-
-                BRK_CUR = allocated;
-                BRK_END = (allocated as *mut u8).add(BRK_MAX_SIZE) as *mut c_void;
+    unsafe fn brk(addr: *mut c_void) -> *mut c_void {
+        // On first invocation, allocate a buffer for brk
+        if BRK_CUR.is_null() {
+            // 4 megabytes of RAM ought to be enough for anybody
+            const BRK_MAX_SIZE: usize = 4 * 1024 * 1024;
+
+            let allocated = Self::mmap(
+                ptr::null_mut(),
+                BRK_MAX_SIZE,
+                PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS,
+                0,
+                0,
+            );
+            if allocated == !0 as *mut c_void
+            /* MAP_FAILED */
+            {
+                return !0 as *mut c_void;
             }
 
-            if addr.is_null() {
-                // Lookup what previous brk() invocations have set the address to
-                BRK_CUR
-            } else if BRK_CUR <= addr && addr < BRK_END {
-                // It's inside buffer, return
-                BRK_CUR = addr;
-                addr
-            } else {
-                // It was outside of valid range
-                ERRNO.set(ENOMEM);
-                ptr::null_mut()
-            }
+            BRK_CUR = allocated;
+            BRK_END = (allocated as *mut u8).add(BRK_MAX_SIZE) as *mut c_void;
         }
-    }
 
-    fn chdir(path: CStr) -> c_int {
-        let path = path_from_c_str!(path);
-        e(path::chdir(path).map(|()| 0)) as c_int
+        if addr.is_null() {
+            // Lookup what previous brk() invocations have set the address to
+            BRK_CUR
+        } else if BRK_CUR <= addr && addr < BRK_END {
+            // It's inside buffer, return
+            BRK_CUR = addr;
+            addr
+        } else {
+            // It was outside of valid range
+            ERRNO.set(ENOMEM);
+            ptr::null_mut()
+        }
     }
 
-    fn set_default_scheme(path: CStr) -> Result<(), Errno> {
+    fn chdir(path: CStr) -> Result<()> {
+        let path = path.to_str().map_err(|_| Errno(EINVAL))?;
+        path::chdir(path)?;
+        Ok(())
+    }
+    fn set_default_scheme(path: CStr) -> Result<()> {
         let path = path.to_str().map_err(|_| Errno(EINVAL))?;
         Ok(path::set_default_scheme(path)?)
     }
 
-    fn chmod(path: CStr, mode: mode_t) -> c_int {
-        match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
-            Ok(file) => Self::fchmod(*file, mode),
-            Err(_) => -1,
-        }
+    fn chmod(path: CStr, mode: mode_t) -> Result<()> {
+        let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
+        Self::fchmod(*file, mode)
     }
 
-    fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
-        match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
-            Ok(file) => Self::fchown(*file, owner, group),
-            Err(_) => -1,
-        }
+    fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> {
+        let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
+        Self::fchown(*file, owner, group)
     }
 
-    // FIXME: unsound
-    fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> c_int {
+    unsafe fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
         // TODO
         eprintln!("relibc clock_getres({}, {:p}): not implemented", clk_id, tp);
-        ERRNO.set(ENOSYS);
-        -1
+        Err(Errno(ENOSYS))
     }
 
-    // FIXME: unsound
-    fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> c_int {
-        unsafe { e(libredox::clock_gettime(clk_id as usize, tp).map(|()| 0)) as c_int }
+    unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
+        libredox::clock_gettime(clk_id as usize, tp)?;
+        Ok(())
     }
 
-    // FIXME: unsound
-    fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> c_int {
+    unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()> {
         // TODO
         eprintln!(
             "relibc clock_settime({}, {:p}): not implemented",
             clk_id, tp
         );
-        ERRNO.set(ENOSYS);
-        -1
+        Err(Errno(ENOSYS))
     }
 
-    fn close(fd: c_int) -> c_int {
-        e(syscall::close(fd as usize)) as c_int
+    fn close(fd: c_int) -> Result<()> {
+        syscall::close(fd as usize)?;
+        Ok(())
     }
 
-    fn dup(fd: c_int) -> c_int {
-        e(syscall::dup(fd as usize, &[])) as c_int
+    fn dup(fd: c_int) -> Result<c_int> {
+        Ok(syscall::dup(fd as usize, &[])? as c_int)
     }
 
-    fn dup2(fd1: c_int, fd2: c_int) -> c_int {
-        e(syscall::dup2(fd1 as usize, fd2 as usize, &[])) as c_int
+    fn dup2(fd1: c_int, fd2: c_int) -> Result<c_int> {
+        Ok(syscall::dup2(fd1 as usize, fd2 as usize, &[])? as c_int)
     }
 
     fn exit(status: c_int) -> ! {
@@ -253,37 +232,33 @@ impl Pal for Sys {
         )) as c_int
     }
 
-    fn fchdir(fd: c_int) -> c_int {
+    fn fchdir(fd: c_int) -> Result<()> {
         let mut buf = [0; 4096];
-        let res = e(syscall::fpath(fd as usize, &mut buf));
-        if res == !0 {
-            !0
-        } else {
-            match str::from_utf8(&buf[..res]) {
-                Ok(path) => e(path::chdir(path).map(|()| 0)) as c_int,
-                Err(_) => {
-                    ERRNO.set(EINVAL);
-                    return -1;
-                }
-            }
-        }
+        let res = syscall::fpath(fd as usize, &mut buf)?;
+
+        let path = str::from_utf8(&buf[..res]).map_err(|_| Errno(EINVAL))?;
+        path::chdir(path)?;
+        Ok(())
     }
 
-    fn fchmod(fd: c_int, mode: mode_t) -> c_int {
-        e(syscall::fchmod(fd as usize, mode as u16)) as c_int
+    fn fchmod(fd: c_int, mode: mode_t) -> Result<()> {
+        syscall::fchmod(fd as usize, mode as u16)?;
+        Ok(())
     }
 
-    fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> c_int {
-        e(syscall::fchown(fd as usize, owner as u32, group as u32)) as c_int
+    fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> Result<()> {
+        syscall::fchown(fd as usize, owner as u32, group as u32)?;
+        Ok(())
     }
 
     fn fcntl(fd: c_int, cmd: c_int, args: c_ulonglong) -> c_int {
         e(syscall::fcntl(fd as usize, cmd as usize, args as usize)) as c_int
     }
 
-    fn fdatasync(fd: c_int) -> c_int {
+    fn fdatasync(fd: c_int) -> Result<()> {
         // TODO: "Needs" syscall update
-        e(syscall::fsync(fd as usize)) as c_int
+        syscall::fsync(fd as usize)?;
+        Ok(())
     }
 
     fn flock(_fd: c_int, _operation: c_int) -> c_int {
@@ -307,22 +282,18 @@ impl Pal for Sys {
         unsafe { e(libredox::fstatvfs(fildes as usize, buf).map(|()| 0)) as c_int }
     }
 
-    fn fsync(fd: c_int) -> Result<(), Errno> {
+    fn fsync(fd: c_int) -> Result<()> {
         syscall::fsync(fd as usize)?;
         Ok(())
     }
 
-    fn ftruncate(fd: c_int, len: off_t) -> Result<(), Errno> {
+    fn ftruncate(fd: c_int, len: off_t) -> Result<()> {
         syscall::ftruncate(fd as usize, len as usize)?;
         Ok(())
     }
 
     #[inline]
-    unsafe fn futex_wait(
-        addr: *mut u32,
-        val: u32,
-        deadline: Option<&timespec>,
-    ) -> Result<(), Errno> {
+    unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<&timespec>) -> Result<()> {
         let deadline = deadline.map(|d| syscall::TimeSpec {
             tv_sec: d.tv_sec,
             tv_nsec: d.tv_nsec as i32,
@@ -331,7 +302,7 @@ impl Pal for Sys {
         Ok(())
     }
     #[inline]
-    unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32, Errno> {
+    unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32> {
         Ok(redox_rt::sys::sys_futex_wake(addr, num)?)
     }
 
@@ -366,7 +337,7 @@ impl Pal for Sys {
         buf
     }
 
-    fn getdents(fd: c_int, buf: &mut [u8], opaque: u64) -> Result<usize, Errno> {
+    fn getdents(fd: c_int, buf: &mut [u8], opaque: u64) -> Result<usize> {
         //println!("GETDENTS {} into ({:p}+{})", fd, buf.as_ptr(), buf.len());
 
         const HEADER_SIZE: usize = size_of::<DirentHeader>();
@@ -425,7 +396,7 @@ impl Pal for Sys {
 
         Ok(record_len.into())
     }
-    fn dir_seek(_fd: c_int, _off: u64) -> Result<(), Errno> {
+    fn dir_seek(_fd: c_int, _off: u64) -> Result<()> {
         // Redox getdents takes an explicit (opaque) offset, so this is a no-op.
         Ok(())
     }
@@ -511,7 +482,7 @@ impl Pal for Sys {
         res
     }
 
-    unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> c_int {
+    unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> Result<()> {
         //TODO
         eprintln!(
             "relibc getrlimit({}, {:p}): not implemented",
@@ -521,17 +492,16 @@ impl Pal for Sys {
             (*rlim).rlim_cur = RLIM_INFINITY;
             (*rlim).rlim_max = RLIM_INFINITY;
         }
-        0
+        Ok(())
     }
 
-    unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> c_int {
+    unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()> {
         //TOOD
         eprintln!(
             "relibc setrlimit({}, {:p}): not implemented",
             resource, rlim
         );
-        ERRNO.set(EPERM);
-        -1
+        Err(Errno(EPERM))
     }
 
     fn getrusage(who: c_int, r_usage: &mut rusage) -> c_int {
@@ -587,70 +557,48 @@ impl Pal for Sys {
         e(syscall::getuid()) as pid_t
     }
 
-    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
+    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> {
         // TODO: Is it correct for regular chown to use O_PATH? On Linux the meaning of that flag
         // is to forbid file operations, including fchown.
 
         // unlike chown, never follow symbolic links
-        match File::open(path, fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW) {
-            Ok(file) => Self::fchown(*file, owner, group),
-            Err(_) => -1,
-        }
+        let file = File::open(path, fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW)?;
+        Self::fchown(*file, owner, group)
     }
 
-    fn link(path1: CStr, path2: CStr) -> c_int {
-        e(unsafe { syscall::link(path1.as_ptr() as *const u8, path2.as_ptr() as *const u8) })
-            as c_int
+    fn link(path1: CStr, path2: CStr) -> Result<()> {
+        unsafe { syscall::link(path1.as_ptr() as *const u8, path2.as_ptr() as *const u8)? };
+        Ok(())
     }
 
-    fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t {
-        e(syscall::lseek(
-            fd as usize,
-            offset as isize,
-            whence as usize,
-        )) as off_t
+    fn lseek(fd: c_int, offset: off_t, whence: c_int) -> Result<off_t> {
+        Ok(syscall::lseek(fd as usize, offset as isize, whence as usize)? as off_t)
     }
 
-    fn mkdir(path: CStr, mode: mode_t) -> c_int {
-        match File::create(
+    fn mkdir(path: CStr, mode: mode_t) -> Result<()> {
+        File::create(
             path,
             fcntl::O_DIRECTORY | fcntl::O_EXCL | fcntl::O_CLOEXEC,
             0o777,
-        ) {
-            Ok(_fd) => 0,
-            Err(_) => -1,
-        }
+        )?;
+        Ok(())
     }
 
-    fn mkfifo(path: CStr, mode: mode_t) -> c_int {
+    fn mkfifo(path: CStr, mode: mode_t) -> Result<()> {
         Sys::mknod(path, syscall::MODE_FIFO as mode_t | (mode & 0o777), 0)
     }
 
-    fn mknodat(dir_fd: c_int, path_name: CStr, mode: mode_t, dev: dev_t) -> c_int {
+    fn mknodat(dir_fd: c_int, path_name: CStr, mode: mode_t, dev: dev_t) -> Result<()> {
         let mut dir_path_buf = [0; 4096];
-        let res = Sys::fpath(dir_fd, &mut dir_path_buf);
-        if res < 0 {
-            return !0;
-        }
+        let res = Sys::fpath(dir_fd, &mut dir_path_buf)?;
 
-        let dir_path = match str::from_utf8(&dir_path_buf[..res as usize]) {
-            Ok(path) => path,
-            Err(_) => {
-                ERRNO.set(EBADR);
-                return !0;
-            }
-        };
+        let dir_path = str::from_utf8(&dir_path_buf[..res as usize]).map_err(|_| Errno(EBADR))?;
 
         let resource_path =
-            match path::canonicalize_using_cwd(Some(&dir_path), &path_name.to_string_lossy()) {
-                Some(path) => path,
-                None => {
-                    // Since parent_dir_path is resolved by fpath, it is more likely that
-                    // the problem was with path.
-                    ERRNO.set(ENOENT);
-                    return !0;
-                }
-            };
+            path::canonicalize_using_cwd(Some(&dir_path), &path_name.to_string_lossy())
+                // Since parent_dir_path is resolved by fpath, it is more likely that
+                // the problem was with path.
+                .ok_or(Errno(ENOENT))?;
 
         Sys::mknod(
             CStr::borrow(&CString::new(resource_path.as_bytes()).unwrap()),
@@ -659,11 +607,9 @@ impl Pal for Sys {
         )
     }
 
-    fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> c_int {
-        match File::create(path, fcntl::O_CREAT | fcntl::O_CLOEXEC, mode) {
-            Ok(fd) => 0,
-            Err(_) => -1,
-        }
+    fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> Result<(), Errno> {
+        File::create(path, fcntl::O_CREAT | fcntl::O_CLOEXEC, mode)?;
+        Ok(())
     }
 
     unsafe fn mlock(addr: *const c_void, len: usize) -> c_int {
@@ -781,7 +727,7 @@ impl Pal for Sys {
         }
     }
 
-    fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int, Errno> {
+    fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int> {
         let path = path.to_str().map_err(|_| Errno(EINVAL))?;
 
         // POSIX states that umask should affect the following:
@@ -796,11 +742,12 @@ impl Pal for Sys {
         Ok(libredox::open(path, oflag, effective_mode)? as c_int)
     }
 
-    fn pipe2(fds: &mut [c_int], flags: c_int) -> c_int {
-        e(extra::pipe2(fds, flags as usize).map(|()| 0)) as c_int
+    fn pipe2(fds: &mut [c_int], flags: c_int) -> Result<()> {
+        extra::pipe2(fds, flags as usize)?;
+        Ok(())
     }
 
-    unsafe fn rlct_clone(stack: *mut usize) -> Result<crate::pthread::OsTid, Errno> {
+    unsafe fn rlct_clone(stack: *mut usize) -> Result<crate::pthread::OsTid> {
         let _guard = clone::rdlock();
         let res = clone::rlct_clone_impl(stack);
 
@@ -809,7 +756,7 @@ impl Pal for Sys {
         })
         .map_err(|error| Errno(error.errno))
     }
-    unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<(), Errno> {
+    unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<()> {
         redox_rt::sys::posix_kill_thread(os_tid.thread_fd, signal as u32)?;
         Ok(())
     }
@@ -819,11 +766,11 @@ impl Pal for Sys {
         }
     }
 
-    fn read(fd: c_int, buf: &mut [u8]) -> Result<ssize_t, Errno> {
+    fn read(fd: c_int, buf: &mut [u8]) -> Result<ssize_t> {
         let fd = usize::try_from(fd).map_err(|_| Errno(EBADF))?;
         Ok(redox_rt::sys::posix_read(fd, buf)? as ssize_t)
     }
-    fn pread(fd: c_int, buf: &mut [u8], offset: off_t) -> Result<ssize_t, Errno> {
+    fn pread(fd: c_int, buf: &mut [u8], offset: off_t) -> Result<ssize_t> {
         unsafe {
             Ok(syscall::syscall5(
                 syscall::SYS_READ2,
@@ -836,27 +783,18 @@ impl Pal for Sys {
         }
     }
 
-    fn fpath(fildes: c_int, out: &mut [u8]) -> ssize_t {
+    fn fpath(fildes: c_int, out: &mut [u8]) -> Result<ssize_t> {
         // Since this is used by realpath, it converts from the old format to the new one for
         // compatibility reasons
         let mut buf = [0; limits::PATH_MAX];
-        let count = match syscall::fpath(fildes as usize, &mut buf) {
-            Ok(ok) => ok,
-            Err(err) => return e(Err(err)) as ssize_t,
-        };
+        let count = syscall::fpath(fildes as usize, &mut buf)?;
 
-        let redox_path = match str::from_utf8(&buf[..count])
+        let redox_path = str::from_utf8(&buf[..count])
             .ok()
             .and_then(|x| redox_path::RedoxPath::from_absolute(x))
-        {
-            Some(some) => some,
-            None => return e(Err(syscall::Error::new(EINVAL))) as ssize_t,
-        };
+            .ok_or(Errno(EINVAL))?;
 
-        let (scheme, reference) = match redox_path.as_parts() {
-            Some(some) => some,
-            None => return e(Err(syscall::Error::new(EINVAL))) as ssize_t,
-        };
+        let (scheme, reference) = redox_path.as_parts().ok_or(Errno(EINVAL))?;
 
         let mut cursor = io::Cursor::new(out);
         let res = match scheme.as_ref() {
@@ -869,109 +807,102 @@ impl Pal for Sys {
             ),
         };
         match res {
-            Ok(()) => cursor.position() as ssize_t,
-            Err(_err) => e(Err(syscall::Error::new(syscall::ENAMETOOLONG))) as ssize_t,
+            Ok(()) => Ok(cursor.position() as ssize_t),
+            Err(_err) => Err(Errno(ENAMETOOLONG)),
         }
     }
 
-    fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t {
-        match File::open(
+    fn readlink(pathname: CStr, out: &mut [u8]) -> Result<ssize_t> {
+        let file = File::open(
             pathname,
             fcntl::O_RDONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
-        ) {
-            Ok(file) => Self::read(*file, out).or_minus_one_errno(),
-            Err(_) => return -1,
-        }
+        )?;
+        Self::read(*file, out)
     }
 
-    fn rename(oldpath: CStr, newpath: CStr) -> c_int {
-        let newpath = path_from_c_str!(newpath);
-        match File::open(oldpath, fcntl::O_PATH | fcntl::O_CLOEXEC) {
-            Ok(file) => e(syscall::frename(*file as usize, newpath)) as c_int,
-            Err(_) => -1,
-        }
+    fn rename(oldpath: CStr, newpath: CStr) -> Result<()> {
+        let newpath = newpath.to_str().map_err(|_| Errno(EINVAL))?;
+
+        let file = File::open(oldpath, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
+        syscall::frename(*file as usize, newpath)?;
+        Ok(())
     }
 
-    fn rmdir(path: CStr) -> c_int {
-        let path = path_from_c_str!(path);
-        e(canonicalize(path).and_then(|path| syscall::rmdir(&path))) as c_int
+    fn rmdir(path: CStr) -> Result<()> {
+        let path = path.to_str().map_err(|_| Errno(EINVAL))?;
+        let canon = canonicalize(path)?;
+        syscall::rmdir(&canon)?;
+        Ok(())
     }
 
-    fn sched_yield() -> c_int {
-        e(syscall::sched_yield()) as c_int
+    fn sched_yield() -> Result<()> {
+        syscall::sched_yield()?;
+        Ok(())
     }
 
-    unsafe fn setgroups(size: size_t, list: *const gid_t) -> c_int {
+    unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()> {
         // TODO
         eprintln!("relibc setgroups({}, {:p}): not implemented", size, list);
-        ERRNO.set(ENOSYS);
-        -1
+        Err(Errno(ENOSYS))
     }
 
-    fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
-        e(syscall::setpgid(pid as usize, pgid as usize)) as c_int
+    fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> {
+        syscall::setpgid(pid as usize, pgid as usize)?;
+        Ok(())
     }
 
-    fn setpriority(which: c_int, who: id_t, prio: c_int) -> c_int {
+    fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()> {
         // TODO
         eprintln!(
             "relibc setpriority({}, {}, {}): not implemented",
             which, who, prio
         );
-        ERRNO.set(ENOSYS);
-        -1
+        Err(Errno(ENOSYS))
     }
 
-    fn setsid() -> c_int {
+    fn setsid() -> Result<()> {
         let session_id = Self::getpid();
-        if session_id < 0 {
-            return -1;
-        }
-        match File::open(
+        assert!(session_id >= 0);
+        let mut file = File::open(
             c_str!("/scheme/thisproc/current/session_id"),
             fcntl::O_WRONLY | fcntl::O_CLOEXEC,
-        ) {
-            Ok(mut file) => match file.write(&usize::to_ne_bytes(session_id.try_into().unwrap())) {
-                Ok(_) => session_id,
-                Err(_) => -1,
-            },
-            Err(_) => -1,
-        }
+        )?;
+        file.write(&usize::to_ne_bytes(session_id.try_into().unwrap()))
+            .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
+        Ok(())
     }
 
-    fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> c_int {
+    fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> {
         if sgid != -1 {
             println!("TODO: suid");
         }
-        e(syscall::setregid(rgid as usize, egid as usize)) as c_int
+        syscall::setregid(rgid as usize, egid as usize)?;
+        Ok(())
     }
 
-    fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> c_int {
+    fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> {
         if suid != -1 {
             println!("TODO: suid");
         }
-        e(syscall::setreuid(ruid as usize, euid as usize)) as c_int
+        syscall::setreuid(ruid as usize, euid as usize)?;
+        Ok(())
     }
 
-    fn symlink(path1: CStr, path2: CStr) -> c_int {
-        let mut file = match File::create(
+    fn symlink(path1: CStr, path2: CStr) -> Result<()> {
+        let mut file = File::create(
             path2,
             fcntl::O_WRONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
             0o777,
-        ) {
-            Ok(ok) => ok,
-            Err(_) => return -1,
-        };
+        )?;
 
-        if file.write(path1.to_bytes()).is_err() {
-            return -1;
-        }
+        file.write(path1.to_bytes())
+            .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
 
-        0
+        Ok(())
     }
 
-    fn sync() -> c_int {
-        0
+    fn sync() -> Result<()> {
+        Ok(())
     }
 
     fn umask(mask: mode_t) -> mode_t {
@@ -979,7 +910,7 @@ impl Pal for Sys {
         (redox_rt::sys::swap_umask(new_effective_mask as u32) as mode_t) & !S_ISVTX
     }
 
-    fn uname(utsname: *mut utsname) -> c_int {
+    unsafe fn uname(utsname: *mut utsname) -> Result<(), Errno> {
         fn gethostname(name: &mut [u8]) -> io::Result<()> {
             if name.is_empty() {
                 return Ok(());
@@ -999,71 +930,63 @@ impl Pal for Sys {
             Ok(())
         }
 
-        fn inner(utsname: *mut utsname) -> Result<(), i32> {
-            match gethostname(unsafe {
-                slice::from_raw_parts_mut(
-                    (*utsname).nodename.as_mut_ptr() as *mut u8,
-                    (*utsname).nodename.len(),
-                )
-            }) {
-                Ok(_) => (),
-                Err(_) => return Err(EIO),
-            }
+        match gethostname(unsafe {
+            slice::from_raw_parts_mut(
+                (*utsname).nodename.as_mut_ptr() as *mut u8,
+                (*utsname).nodename.len(),
+            )
+        }) {
+            Ok(_) => (),
+            Err(_) => return Err(Errno(EIO)),
+        }
 
-            let file_path = c_str!("/scheme/sys/uname");
-            let mut file = match File::open(file_path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
-                Ok(ok) => ok,
-                Err(_) => return Err(EIO),
-            };
-            let mut lines = BufReader::new(&mut file).lines();
-
-            let mut read_line = |dst: &mut [c_char]| {
-                let line = match lines.next() {
-                    Some(Ok(l)) => match CString::new(l) {
-                        Ok(l) => l,
-                        Err(_) => return Err(EIO),
-                    },
-                    None | Some(Err(_)) => return Err(EIO),
-                };
-
-                let line_slice: &[c_char] = unsafe { mem::transmute(line.as_bytes_with_nul()) };
-
-                if line_slice.len() <= UTSLENGTH {
-                    dst[..line_slice.len()].copy_from_slice(line_slice);
-                    Ok(())
-                } else {
-                    Err(EIO)
-                }
+        let file_path = c_str!("/scheme/sys/uname");
+        let mut file = match File::open(file_path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
+            Ok(ok) => ok,
+            Err(_) => return Err(Errno(EIO)),
+        };
+        let mut lines = BufReader::new(&mut file).lines();
+
+        let mut read_line = |dst: &mut [c_char]| {
+            let line = match lines.next() {
+                Some(Ok(l)) => match CString::new(l) {
+                    Ok(l) => l,
+                    Err(_) => return Err(Errno(EIO)),
+                },
+                None | Some(Err(_)) => return Err(Errno(EIO)),
             };
 
-            unsafe {
-                read_line(&mut (*utsname).sysname)?;
-                read_line(&mut (*utsname).release)?;
-                read_line(&mut (*utsname).machine)?;
+            let line_slice: &[c_char] = unsafe { mem::transmute(line.as_bytes_with_nul()) };
 
-                // Version is not provided
-                ptr::write_bytes((*utsname).version.as_mut_ptr(), 0, UTSLENGTH);
-
-                // Redox doesn't provide domainname in sys:uname
-                //read_line(&mut (*utsname).domainname)?;
-                ptr::write_bytes((*utsname).domainname.as_mut_ptr(), 0, UTSLENGTH);
+            if line_slice.len() <= UTSLENGTH {
+                dst[..line_slice.len()].copy_from_slice(line_slice);
+                Ok(())
+            } else {
+                Err(Errno(EIO))
             }
+        };
 
-            Ok(())
-        }
+        unsafe {
+            read_line(&mut (*utsname).sysname)?;
+            read_line(&mut (*utsname).release)?;
+            read_line(&mut (*utsname).machine)?;
 
-        match inner(utsname) {
-            Ok(()) => 0,
-            Err(err) => {
-                ERRNO.set(err);
-                -1
-            }
+            // Version is not provided
+            ptr::write_bytes((*utsname).version.as_mut_ptr(), 0, UTSLENGTH);
+
+            // Redox doesn't provide domainname in sys:uname
+            //read_line(&mut (*utsname).domainname)?;
+            ptr::write_bytes((*utsname).domainname.as_mut_ptr(), 0, UTSLENGTH);
         }
+
+        Ok(())
     }
 
-    fn unlink(path: CStr) -> c_int {
-        let path = path_from_c_str!(path);
-        e(canonicalize(path).and_then(|path| syscall::unlink(&path))) as c_int
+    fn unlink(path: CStr) -> Result<()> {
+        let path = path.to_str().map_err(|_| Errno(EINVAL))?;
+        let canon = canonicalize(path)?;
+        syscall::unlink(&canon)?;
+        Ok(())
     }
 
     fn waitpid(mut pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t {
@@ -1125,11 +1048,11 @@ impl Pal for Sys {
         res as pid_t
     }
 
-    fn write(fd: c_int, buf: &[u8]) -> Result<ssize_t, Errno> {
+    fn write(fd: c_int, buf: &[u8]) -> Result<ssize_t> {
         let fd = usize::try_from(fd).map_err(|_| Errno(EBADFD))?;
         Ok(redox_rt::sys::posix_write(fd, buf)? as ssize_t)
     }
-    fn pwrite(fd: c_int, buf: &[u8], offset: off_t) -> Result<ssize_t, Errno> {
+    fn pwrite(fd: c_int, buf: &[u8], offset: off_t) -> Result<ssize_t> {
         unsafe {
             Ok(syscall::syscall5(
                 syscall::SYS_WRITE2,
diff --git a/src/sync/semaphore.rs b/src/sync/semaphore.rs
index c0a0b3a2ad7a4dc9ddc9ef10bc573c9368a1018d..34a1283b0592b75f74859453f0887108220a639d 100644
--- a/src/sync/semaphore.rs
+++ b/src/sync/semaphore.rs
@@ -61,7 +61,7 @@ impl Semaphore {
 
             if let Some(timeout) = timeout_opt {
                 let mut time = timespec::default();
-                clock_gettime(CLOCK_MONOTONIC, &mut time);
+                unsafe { clock_gettime(CLOCK_MONOTONIC, &mut time) };
                 if (time.tv_sec > timeout.tv_sec)
                     || (time.tv_sec == timeout.tv_sec && time.tv_nsec >= timeout.tv_nsec)
                 {