diff --git a/include/math.h b/include/math.h
index 5ce9f25af7bb3ffa55b39cfee699360b541a50a2..071e3daa7cf21b98119f06d262deca1655807b8d 100644
--- a/include/math.h
+++ b/include/math.h
@@ -1,2 +1,3 @@
 #define OPENLIBM_USE_HOST_FENV_H 1
 #include <openlibm.h>
+#undef I
diff --git a/src/platform/src/linux/mod.rs b/src/platform/src/linux/mod.rs
index f4b0116cb91792bd2a656d4472263f8949379b59..14765b6c5a5bfa5d61860f74d7d28ec5303a3461 100644
--- a/src/platform/src/linux/mod.rs
+++ b/src/platform/src/linux/mod.rs
@@ -25,6 +25,10 @@ pub unsafe fn accept(socket: c_int, address: *mut sockaddr, address_len: *mut so
     e(syscall!(ACCEPT, socket, address, address_len)) as c_int
 }
 
+pub fn access(path: *const c_char, mode: c_int) -> c_int {
+    e(unsafe { syscall!(ACCESS, path, mode) }) as c_int
+}
+
 pub unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> c_int {
     e(syscall!(BIND, socket, address, address_len)) as c_int
 }
diff --git a/src/platform/src/redox/mod.rs b/src/platform/src/redox/mod.rs
index 80a29e95bb4545774c8c971994be26df519cf18a..75cbc771fa6de89e534e48a8d644b639d07066d0 100644
--- a/src/platform/src/redox/mod.rs
+++ b/src/platform/src/redox/mod.rs
@@ -12,6 +12,8 @@ use syscall::{self, Result};
 use types::*;
 use *;
 
+const EINVAL: c_int = 22;
+
 #[thread_local]
 static mut SIG_HANDLER: Option<extern "C" fn(c_int)> = None;
 
@@ -82,6 +84,51 @@ pub unsafe fn accept(socket: c_int, address: *mut sockaddr, address_len: *mut so
     stream
 }
 
+pub fn access(path: *const c_char, mode: c_int) -> c_int {
+    let fd = match RawFile::open(path, 0, 0) {
+        Ok(fd) => fd,
+        Err(_) => return -1
+    };
+    if mode == F_OK {
+        return 0;
+    }
+
+    let mut stat = syscall::Stat::default();
+
+    if e(syscall::fstat(*fd as usize, &mut stat)) == !0 {
+        return -1;
+    }
+
+    let uid = e(syscall::getuid());
+    if uid == !0 {
+        return -1;
+    }
+    let gid = e(syscall::getgid());
+    if gid == !0 {
+        return -1;
+    }
+
+    let perms = if stat.st_uid as usize == uid {
+        // octal has max 7 characters, binary has max two. And we're interested
+        // in the 3rd digit
+        stat.st_mode >> ((7 / 2) * 2 & 0o7)
+    } else if stat.st_gid as usize == gid {
+        stat.st_mode >> ((7 / 2) & 0o7)
+    } else {
+        stat.st_mode & 0o7
+    };
+    if (mode & R_OK == R_OK && perms & 0o4 != 0o4)
+            || (mode & W_OK == W_OK && perms & 0o2 != 0o2)
+            || (mode & X_OK == X_OK && perms & 0o1 != 0o1) {
+        unsafe {
+            errno = EINVAL;
+        }
+        return -1;
+    }
+
+    0
+}
+
 pub unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> c_int {
     bind_or_connect!(bind socket, address, address_len)
 }
@@ -253,15 +300,15 @@ pub fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
                     (*buf).st_blksize = redox_buf.st_blksize as blksize_t;
                     (*buf).st_atim = timespec {
                         tv_sec: redox_buf.st_atime as time_t,
-                        tv_nsec: 0,
+                        tv_nsec: redox_buf.st_atime_nsec as c_long,
                     };
                     (*buf).st_mtim = timespec {
                         tv_sec: redox_buf.st_mtime as time_t,
-                        tv_nsec: 0,
+                        tv_nsec: redox_buf.st_mtime_nsec as c_long,
                     };
                     (*buf).st_ctim = timespec {
                         tv_sec: redox_buf.st_ctime as time_t,
-                        tv_nsec: 0,
+                        tv_nsec: redox_buf.st_ctime_nsec as c_long,
                     };
                 }
             }
diff --git a/src/platform/src/types.rs b/src/platform/src/types.rs
index dbd5364b57c719011ceee36f5eb22544f9f7d4cc..5ac9ab3a3071b2f65e389ace02988575e69df2c3 100644
--- a/src/platform/src/types.rs
+++ b/src/platform/src/types.rs
@@ -236,3 +236,8 @@ pub const FD_SETSIZE: usize = 1024;
 pub struct fd_set {
     pub fds_bits: [c_ulong; FD_SETSIZE / (8 * mem::size_of::<c_ulong>())],
 }
+
+pub const F_OK: c_int = 0;
+pub const R_OK: c_int = 4;
+pub const W_OK: c_int = 2;
+pub const X_OK: c_int = 1;
diff --git a/src/sys_file/src/lib.rs b/src/sys_file/src/lib.rs
index 2ffb3e04bcdf2cd93fbf0428e30d9e6b555200f5..a12bcc2a0bee2b1a3b71c543389df10d0a3e1c7b 100644
--- a/src/sys_file/src/lib.rs
+++ b/src/sys_file/src/lib.rs
@@ -7,9 +7,9 @@ extern crate platform;
 use platform::types::*;
 
 pub const LOCK_SH: usize = 1;
-pub const LOCK_EX: usize = 1 << 1;
-pub const LOCK_NB: usize = 1 << 2;
-pub const LOCK_UN: usize = 1 << 3;
+pub const LOCK_EX: usize = 2;
+pub const LOCK_NB: usize = 4;
+pub const LOCK_UN: usize = 8;
 
 pub const L_SET: usize = 0;
 pub const L_INCR: usize = 1;
diff --git a/src/sys_socket/src/constants.rs b/src/sys_socket/src/constants.rs
index 6abb1b8e64daaeaf6b79a35fb8c405d2d23e23c7..38a791c156c6ffc0de120def4832ac0cd28636db 100644
--- a/src/sys_socket/src/constants.rs
+++ b/src/sys_socket/src/constants.rs
@@ -43,13 +43,13 @@ pub const SO_DOMAIN: c_int = 39;
 
 pub const SOMAXCONN: c_int = 128;
 
-pub const MSG_CTRUNC: c_int = 1 << 3;
-pub const MSG_DONTROUTE: c_int = 1 << 2;
-pub const MSG_EOR: c_int = 1 << 7;
+pub const MSG_CTRUNC: c_int = 8;
+pub const MSG_DONTROUTE: c_int = 4;
+pub const MSG_EOR: c_int = 128;
 pub const MSG_OOB: c_int = 1;
-pub const MSG_PEEK: c_int = 1 << 1;
-pub const MSG_TRUNC: c_int = 1 << 5;
-pub const MSG_WAITALL: c_int = 1 << 8;
+pub const MSG_PEEK: c_int = 2;
+pub const MSG_TRUNC: c_int = 32;
+pub const MSG_WAITALL: c_int = 256;
 
 pub const AF_INET6: c_int = 10;
 pub const AF_UNIX: c_int = 1;
diff --git a/src/unistd/src/lib.rs b/src/unistd/src/lib.rs
index 76dbb27b1f4ed737f3fb4e6ffb5ee77bb5fbf24f..95c3eac42228e5aca3fcee80a0bf1cb6187fe416 100644
--- a/src/unistd/src/lib.rs
+++ b/src/unistd/src/lib.rs
@@ -20,10 +20,10 @@ mod brk;
 mod getopt;
 mod pathconf;
 
-pub const R_OK: c_int = 1;
+pub const F_OK: c_int = 0;
+pub const R_OK: c_int = 4;
 pub const W_OK: c_int = 2;
-pub const X_OK: c_int = 4;
-pub const F_OK: c_int = 8;
+pub const X_OK: c_int = 1;
 
 pub const SEEK_SET: c_int = 0;
 pub const SEEK_CUR: c_int = 1;
@@ -43,9 +43,9 @@ pub extern "C" fn _exit(status: c_int) {
     platform::exit(status)
 }
 
-// #[no_mangle]
-pub extern "C" fn access(path: *const c_char, amode: c_int) -> c_int {
-    unimplemented!();
+#[no_mangle]
+pub extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
+    platform::access(path, mode)
 }
 
 #[no_mangle]
diff --git a/tests/Makefile b/tests/Makefile
index b2bee9f19439987db2a2557b6a450d1819ac649e..cad834e36194176af396e60b0f53f15b6fd68521 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -46,6 +46,7 @@ EXPECT_BINS=\
 	time/mktime \
 	time/strftime \
 	time/time \
+	unistd/access \
 	unistd/brk \
 	unistd/dup \
 	unistd/exec \
diff --git a/tests/expected/unistd/access.stderr b/tests/expected/unistd/access.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/unistd/access.stdout b/tests/expected/unistd/access.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/unistd/access.c b/tests/unistd/access.c
new file mode 100644
index 0000000000000000000000000000000000000000..9491aa5f5de4648cdea9248af6ee04b34298b3f5
--- /dev/null
+++ b/tests/unistd/access.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int main() {
+    if (access("example_dir/1-never-gonna-give-you-up", R_OK | W_OK)) {
+        perror("access");
+        return 1;
+    }
+    if (!access("example_dir/1-never-gonna-give-you-up", X_OK)) {
+        puts("Accessing a file with X_OK worked even though it... probably... shouldn't?");
+        puts("Please run `chmod 644 example_dir/*` and try again.");
+        return 1;
+    }
+}