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 f17f4029926cd359d121922b1bbb217687377192..76b31c307e2b424921d913e96b6d5f5d725294a6 100644
--- a/src/fs.rs
+++ b/src/fs.rs
@@ -90,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/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 3b93de68a77c5ed6883ed63ae47ff8ce747182c3..e36a182db69fde296dfb3c048d1380ff5c9c000f 100644
--- a/src/header/stdio/mod.rs
+++ b/src/header/stdio/mod.rs
@@ -682,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;
     }
@@ -713,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;
     }
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
index 3c1abe687715766c23376d681394c4c0a1a56720..47dd99e5eb3c8ba75925a3270441b841e33d8b8e 100644
--- a/src/header/stdlib/mod.rs
+++ b/src/header/stdlib/mod.rs
@@ -985,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 cd29535c3b4cae9279b65b62f710a4fc000f2cfd..17dfe6bcd339630422fce1e2b4cb433a548dc69a 100644
--- a/src/header/sys_stat/mod.rs
+++ b/src/header/sys_stat/mod.rs
@@ -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/unistd/mod.rs b/src/header/unistd/mod.rs
index 7b2908cb3b68102a32cd7786cd19a8f16cb25874..03ef809cbbed04d34049495d7c1763c38533a963 100644
--- a/src/header/unistd/mod.rs
+++ b/src/header/unistd/mod.rs
@@ -453,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;
@@ -558,7 +561,7 @@ pub unsafe extern "C" fn lchown(path: *const c_char, owner: uid_t, group: gid_t)
 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]
@@ -602,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]
@@ -623,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]
@@ -694,7 +699,7 @@ 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]
@@ -706,16 +711,18 @@ pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int {
 #[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]
@@ -726,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]
@@ -782,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]
@@ -840,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();
     }
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 1cf5f09a4f1220b70972f20bcf45e51fcf264387..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,7 +88,7 @@ impl Sys {
 }
 
 impl Pal for Sys {
-    fn access(path: CStr, mode: c_int) -> Result<(), Errno> {
+    fn access(path: CStr, mode: c_int) -> Result<()> {
         e_raw(unsafe { syscall!(ACCESS, path.as_ptr(), mode) }).map(|_| ())
     }
 
@@ -96,18 +96,18 @@ impl Pal for Sys {
         unsafe { syscall!(BRK, addr) as *mut c_void }
     }
 
-    fn chdir(path: CStr) -> Result<(), Errno> {
+    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) -> Result<(), Errno> {
+
+    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) -> Result<(), Errno> {
+    fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> {
         e_raw(unsafe {
             syscall!(
                 FCHOWNAT,
@@ -120,27 +120,27 @@ impl Pal for Sys {
         .map(|_| ())
     }
 
-    unsafe fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> Result<(), Errno> {
+    unsafe fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
         e_raw(syscall!(CLOCK_GETRES, clk_id, tp)).map(|_| ())
     }
 
-    unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> Result<(), Errno> {
+    unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
         e_raw(syscall!(CLOCK_GETTIME, clk_id, tp)).map(|_| ())
     }
 
-    unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<(), Errno> {
+    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) -> Result<(), Errno> {
+    fn close(fildes: c_int) -> Result<()> {
         e_raw(unsafe { syscall!(CLOSE, fildes) }).map(|_| ())
     }
 
-    fn dup(fildes: c_int) -> Result<c_int, Errno> {
+    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) -> Result<c_int, Errno> {
+    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)
     }
 
@@ -162,19 +162,19 @@ impl Pal for Sys {
         Self::exit(0)
     }
 
-    fn fchdir(fildes: c_int) -> Result<(), Errno> {
+    fn fchdir(fildes: c_int) -> Result<()> {
         e_raw(unsafe { syscall!(FCHDIR, fildes) }).map(|_| ())
     }
 
-    fn fchmod(fildes: c_int, mode: mode_t) -> Result<(), Errno> {
+    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) -> Result<(), Errno> {
+    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) -> Result<(), Errno> {
+    fn fdatasync(fildes: c_int) -> Result<()> {
         e_raw(unsafe { syscall!(FDATASYNC, fildes) }).map(|_| ())
     }
 
@@ -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) -> Result<(), Errno> {
+    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,51 +553,52 @@ 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) -> Result<(), Errno> {
+    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) -> Result<(), Errno> {
+    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() -> Result<(), Errno> {
+    fn sync() -> Result<()> {
         e_raw(unsafe { syscall!(SYNC) }).map(|_| ())
     }
 
@@ -608,11 +606,11 @@ impl Pal for Sys {
         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) -> Result<(), Errno> {
+    fn unlink(path: CStr) -> Result<()> {
         e_raw(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), 0) }).map(|_| ())
     }
 
@@ -620,10 +618,10 @@ impl Pal for Sys {
         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 be3746034bc415a4167ad57d4e085f6228004a07..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,
@@ -25,8 +25,6 @@ mod signal;
 pub use self::socket::PalSocket;
 mod socket;
 
-type Result<T, E = Errno> = core::result::Result<T, E>;
-
 pub trait Pal {
     fn access(path: CStr, mode: c_int) -> Result<()>;
 
@@ -77,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;
 
@@ -96,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
@@ -127,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;
 
@@ -141,19 +135,19 @@ pub trait Pal {
 
     fn getuid() -> uid_t;
 
-    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<(), Errno>;
+    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;
 
@@ -190,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) -> Result<()>;
 
     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() -> 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) -> 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 c48120d2c8556262570f20168cfc3e61245bb2aa..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,7 +87,7 @@ pub fn e(sys: Result<usize>) -> usize {
 pub struct Sys;
 
 impl Pal for Sys {
-    fn access(path: CStr, mode: c_int) -> Result<(), Errno> {
+    fn access(path: CStr, mode: c_int) -> Result<()> {
         let fd = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
 
         if mode == F_OK {
@@ -156,39 +156,38 @@ impl Pal for Sys {
         }
     }
 
-    fn chdir(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<(), Errno> {
+    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) -> Result<(), Errno> {
+    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) -> Result<(), Errno> {
+    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)
     }
 
-    unsafe fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> Result<(), Errno> {
+    unsafe fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
         // TODO
         eprintln!("relibc clock_getres({}, {:p}): not implemented", clk_id, tp);
         Err(Errno(ENOSYS))
     }
 
-    unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> Result<(), Errno> {
+    unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> Result<()> {
         libredox::clock_gettime(clk_id as usize, tp)?;
         Ok(())
     }
 
-    unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<(), Errno> {
+    unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()> {
         // TODO
         eprintln!(
             "relibc clock_settime({}, {:p}): not implemented",
@@ -197,16 +196,16 @@ impl Pal for Sys {
         Err(Errno(ENOSYS))
     }
 
-    fn close(fd: c_int) -> Result<(), Errno> {
+    fn close(fd: c_int) -> Result<()> {
         syscall::close(fd as usize)?;
         Ok(())
     }
 
-    fn dup(fd: c_int) -> Result<c_int, Errno> {
+    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) -> Result<c_int, Errno> {
+    fn dup2(fd1: c_int, fd2: c_int) -> Result<c_int> {
         Ok(syscall::dup2(fd1 as usize, fd2 as usize, &[])? as c_int)
     }
 
@@ -233,7 +232,7 @@ impl Pal for Sys {
         )) as c_int
     }
 
-    fn fchdir(fd: c_int) -> Result<(), Errno> {
+    fn fchdir(fd: c_int) -> Result<()> {
         let mut buf = [0; 4096];
         let res = syscall::fpath(fd as usize, &mut buf)?;
 
@@ -242,12 +241,12 @@ impl Pal for Sys {
         Ok(())
     }
 
-    fn fchmod(fd: c_int, mode: mode_t) -> Result<(), Errno> {
+    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) -> Result<(), Errno> {
+    fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> Result<()> {
         syscall::fchown(fd as usize, owner as u32, group as u32)?;
         Ok(())
     }
@@ -256,7 +255,7 @@ impl Pal for Sys {
         e(syscall::fcntl(fd as usize, cmd as usize, args as usize)) as c_int
     }
 
-    fn fdatasync(fd: c_int) -> Result<(), Errno> {
+    fn fdatasync(fd: c_int) -> Result<()> {
         // TODO: "Needs" syscall update
         syscall::fsync(fd as usize)?;
         Ok(())
@@ -283,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,
@@ -307,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)?)
     }
 
@@ -342,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>();
@@ -401,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(())
     }
@@ -487,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",
@@ -497,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 {
@@ -563,7 +557,7 @@ impl Pal for Sys {
         e(syscall::getuid()) as pid_t
     }
 
-    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<(), Errno> {
+    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.
 
@@ -572,59 +566,39 @@ impl Pal for Sys {
         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()),
@@ -633,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 {
@@ -755,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:
@@ -770,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);
 
@@ -783,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(())
     }
@@ -793,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,
@@ -810,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() {
@@ -843,22 +807,20 @@ 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) -> Result<(), Errno> {
+    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)?;
@@ -866,87 +828,80 @@ impl Pal for Sys {
         Ok(())
     }
 
-    fn rmdir(path: CStr) -> Result<(), Errno> {
+    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() -> Result<(), Errno> {
+    fn sync() -> Result<()> {
         Ok(())
     }
 
@@ -955,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(());
@@ -975,69 +930,59 @@ 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) -> Result<(), Errno> {
+    fn unlink(path: CStr) -> Result<()> {
         let path = path.to_str().map_err(|_| Errno(EINVAL))?;
         let canon = canonicalize(path)?;
         syscall::unlink(&canon)?;
@@ -1103,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,