diff --git a/scheme/debug.rs b/scheme/debug.rs
index eed44ede96005fca72466ea1fbb882c9f9360105..52d9a8bf53778d213457aa949402c4fba67d5b74 100644
--- a/scheme/debug.rs
+++ b/scheme/debug.rs
@@ -33,7 +33,7 @@ pub extern fn debug_input(b: u8) {
 pub struct DebugScheme;
 
 impl Scheme for DebugScheme {
-    fn open(&self, _path: &[u8], _flags: usize) -> Result<usize> {
+    fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
         Ok(0)
     }
 
diff --git a/scheme/env.rs b/scheme/env.rs
index 807c2736887b950ec3c9de47b537b4ce48614609..4a51485001fa96755ed3b68459e8342b71c0146d 100644
--- a/scheme/env.rs
+++ b/scheme/env.rs
@@ -32,7 +32,7 @@ impl EnvScheme {
 }
 
 impl Scheme for EnvScheme {
-    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+    fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
         let path = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?.trim_matches('/');
 
         let env_lock = {
diff --git a/scheme/event.rs b/scheme/event.rs
index 2dead8d3696e4381bae10c391eee17fb93f49ea0..8229559c4e2cd0e4d3dc67bd3c5b27a1e56c4119 100644
--- a/scheme/event.rs
+++ b/scheme/event.rs
@@ -24,7 +24,7 @@ impl EventScheme {
 }
 
 impl Scheme for EventScheme {
-    fn open(&self, _path: &[u8], _flags: usize) -> Result<usize> {
+    fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
         let handle = {
             let contexts = context::contexts();
             let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
diff --git a/scheme/initfs.rs b/scheme/initfs.rs
index c3a795c8bfa241a8b64e82650941b57148b03055..18696ed0f8fee306fc6d867e8ecaa0f214d95f40 100644
--- a/scheme/initfs.rs
+++ b/scheme/initfs.rs
@@ -35,7 +35,7 @@ impl InitFsScheme {
 }
 
 impl Scheme for InitFsScheme {
-    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+    fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
         let path_utf8 = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?;
         let path_trimmed = path_utf8.trim_matches('/');
 
@@ -46,7 +46,7 @@ impl Scheme for InitFsScheme {
                 self.handles.write().insert(id, Handle {
                     path: entry.0,
                     data: (entry.1).0,
-                    mode: if (entry.1).1 { MODE_DIR } else { MODE_FILE },
+                    mode: if (entry.1).1 { MODE_DIR |  0o755 } else { MODE_FILE | 0o744 },
                     seek: 0
                 });
 
@@ -130,6 +130,8 @@ impl Scheme for InitFsScheme {
         let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
 
         stat.st_mode = handle.mode;
+        stat.st_uid = 0;
+        stat.st_gid = 0;
         stat.st_size = handle.data.len() as u64;
 
         Ok(0)
diff --git a/scheme/irq.rs b/scheme/irq.rs
index 7ec202ab88ac4cf32f6eb4f06006b24f79220d35..62fcb1aff66b8281b189816af682ac5b116418b2 100644
--- a/scheme/irq.rs
+++ b/scheme/irq.rs
@@ -7,7 +7,7 @@ use syscall::scheme::Scheme;
 pub struct IrqScheme;
 
 impl Scheme for IrqScheme {
-    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+    fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
         let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
 
         let id = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
diff --git a/scheme/root.rs b/scheme/root.rs
index e982c8f41b92c294d2c1b7827a01dc602efee7d1..c088d6e2f6c743a79a98d124f43e91270dd22c89 100644
--- a/scheme/root.rs
+++ b/scheme/root.rs
@@ -25,7 +25,7 @@ impl RootScheme {
 }
 
 impl Scheme for RootScheme {
-    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+    fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
         let context = {
             let contexts = context::contexts();
             let context = contexts.current().ok_or(Error::new(ESRCH))?;
diff --git a/scheme/user.rs b/scheme/user.rs
index 96ba87e78a31cdc7d2ccb73e5f934462f753ae7d..542f01e651127402bad5c37813d05c4efe8ecd52 100644
--- a/scheme/user.rs
+++ b/scheme/user.rs
@@ -218,7 +218,7 @@ impl UserScheme {
 }
 
 impl Scheme for UserScheme {
-    fn open(&self, path: &[u8], flags: usize) -> Result<usize> {
+    fn open(&self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
         let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
         let address = inner.capture(path)?;
         let result = inner.call(SYS_OPEN, address, path.len(), flags);
@@ -226,15 +226,15 @@ impl Scheme for UserScheme {
         result
     }
 
-    fn mkdir(&self, path: &[u8], mode: usize) -> Result<usize> {
+    fn mkdir(&self, path: &[u8], mode: u16, _uid: u32, _gid: u32) -> Result<usize> {
         let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
         let address = inner.capture(path)?;
-        let result = inner.call(SYS_MKDIR, address, path.len(), mode);
+        let result = inner.call(SYS_MKDIR, address, path.len(), mode as usize);
         let _ = inner.release(address);
         result
     }
 
-    fn rmdir(&self, path: &[u8]) -> Result<usize> {
+    fn rmdir(&self, path: &[u8], _uid: u32, _gid: u32) -> Result<usize> {
         let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
         let address = inner.capture(path)?;
         let result = inner.call(SYS_RMDIR, address, path.len(), 0);
@@ -242,7 +242,7 @@ impl Scheme for UserScheme {
         result
     }
 
-    fn unlink(&self, path: &[u8]) -> Result<usize> {
+    fn unlink(&self, path: &[u8], _uid: u32, _gid: u32) -> Result<usize> {
         let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
         let address = inner.capture(path)?;
         let result = inner.call(SYS_UNLINK, address, path.len(), 0);
diff --git a/syscall/fs.rs b/syscall/fs.rs
index adb690d4abe358b10bbf20ebc0a9adce25de8ddf..1c969f6791d47736ba799ed56528f7e468379497 100644
--- a/syscall/fs.rs
+++ b/syscall/fs.rs
@@ -2,7 +2,7 @@
 
 use context;
 use scheme;
-use syscall::data::{Packet, Stat};
+use syscall::data::Packet;
 use syscall::error::*;
 
 pub fn file_op(a: usize, fd: usize, c: usize, d: usize) -> Result<usize> {
@@ -70,11 +70,11 @@ pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
 
 /// Open syscall
 pub fn open(path: &[u8], flags: usize) -> Result<usize> {
-    let path_canon = {
+    let (path_canon, uid, gid) = {
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        context.canonicalize(path)
+        (context.canonicalize(path), context.uid, context.gid)
     };
 
     let mut parts = path_canon.splitn(2, |&b| b == b':');
@@ -88,7 +88,7 @@ pub fn open(path: &[u8], flags: usize) -> Result<usize> {
             let (scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENOENT))?;
             (scheme_id, scheme.clone())
         };
-        let file_id = scheme.open(reference_opt.unwrap_or(b""), flags)?;
+        let file_id = scheme.open(reference_opt.unwrap_or(b""), flags, uid, gid)?;
         (scheme_id, file_id)
     };
 
@@ -102,12 +102,12 @@ pub fn open(path: &[u8], flags: usize) -> Result<usize> {
 }
 
 /// mkdir syscall
-pub fn mkdir(path: &[u8], mode: usize) -> Result<usize> {
-    let path_canon = {
+pub fn mkdir(path: &[u8], mode: u16) -> Result<usize> {
+    let (path_canon, uid, gid) = {
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        context.canonicalize(path)
+        (context.canonicalize(path), context.uid, context.gid)
     };
 
     let mut parts = path_canon.splitn(2, |&b| b == b':');
@@ -120,16 +120,16 @@ pub fn mkdir(path: &[u8], mode: usize) -> Result<usize> {
         let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENOENT))?;
         scheme.clone()
     };
-    scheme.mkdir(reference_opt.unwrap_or(b""), mode)
+    scheme.mkdir(reference_opt.unwrap_or(b""), mode, uid, gid)
 }
 
 /// rmdir syscall
 pub fn rmdir(path: &[u8]) -> Result<usize> {
-    let path_canon = {
+    let (path_canon, uid, gid) = {
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        context.canonicalize(path)
+        (context.canonicalize(path), context.uid, context.gid)
     };
 
     let mut parts = path_canon.splitn(2, |&b| b == b':');
@@ -142,16 +142,16 @@ pub fn rmdir(path: &[u8]) -> Result<usize> {
         let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENOENT))?;
         scheme.clone()
     };
-    scheme.rmdir(reference_opt.unwrap_or(b""))
+    scheme.rmdir(reference_opt.unwrap_or(b""), uid, gid)
 }
 
 /// Unlink syscall
 pub fn unlink(path: &[u8]) -> Result<usize> {
-    let path_canon = {
+    let (path_canon, uid, gid) = {
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        context.canonicalize(path)
+        (context.canonicalize(path), context.uid, context.gid)
     };
 
     let mut parts = path_canon.splitn(2, |&b| b == b':');
@@ -164,7 +164,7 @@ pub fn unlink(path: &[u8]) -> Result<usize> {
         let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENOENT))?;
         scheme.clone()
     };
-    scheme.unlink(reference_opt.unwrap_or(b""))
+    scheme.unlink(reference_opt.unwrap_or(b""), uid, gid)
 }
 
 /// Close syscall
diff --git a/syscall/mod.rs b/syscall/mod.rs
index f10469e1e20f5ea5d84b0bd835b88ee588d12554..c0d389ef93b10f92d3c44eebe405c49784d26f87 100644
--- a/syscall/mod.rs
+++ b/syscall/mod.rs
@@ -37,6 +37,9 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
             },
             SYS_CLASS_PATH => match a {
                 SYS_OPEN => open(validate_slice(b as *const u8, c)?, d),
+                SYS_MKDIR => mkdir(validate_slice(b as *const u8, c)?, d as u16),
+                SYS_RMDIR => rmdir(validate_slice(b as *const u8, c)?),
+                SYS_UNLINK => unlink(validate_slice(b as *const u8, c)?),
                 _ => unreachable!()
             },
             _ => match a {
diff --git a/syscall/process.rs b/syscall/process.rs
index 3821b355f9fb59de280d5fb8f3707df878bde2c5..9519ef2bff1aab7c40de05f61196243d9e4190fa 100644
--- a/syscall/process.rs
+++ b/syscall/process.rs
@@ -432,9 +432,33 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
             args.push(arg.to_vec()); // Must be moved into kernel space before exec unmaps all memory
         }
 
+        let (uid, gid) = {
+            let contexts = context::contexts();
+            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+            let context = context_lock.read();
+            (context.uid, context.gid)
+        };
+
         let file = syscall::open(path, 0)?;
         let mut stat = Stat::default();
         syscall::file_op_mut_slice(syscall::number::SYS_FSTAT, file, &mut stat)?;
+
+        let mut perm = stat.st_mode & 0o7;
+        if stat.st_uid == uid {
+            perm |= (stat.st_mode >> 6) & 0o7;
+        }
+        if stat.st_gid == gid {
+            perm |= (stat.st_mode >> 3) & 0o7;
+        }
+        if uid == 0 {
+            perm |= 0o7;
+        }
+
+        if perm & 0o1 != 0o1 {
+            let _ = syscall::close(file);
+            return Err(Error::new(EACCES));
+        }
+
         //TODO: Only read elf header, not entire file. Then read required segments
         let mut data = vec![0; stat.st_size as usize];
         syscall::file_op_mut_slice(syscall::number::SYS_READ, file, &mut data)?;
@@ -458,6 +482,14 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
                     drop(context.stack.take());
                     context.grants = Arc::new(Mutex::new(Vec::new()));
 
+                    if stat.st_mode & syscall::flag::MODE_SETUID == syscall::flag::MODE_SETUID {
+                        context.uid = stat.st_uid;
+                    }
+
+                    if stat.st_mode & syscall::flag::MODE_SETGID == syscall::flag::MODE_SETGID {
+                        context.gid = stat.st_gid;
+                    }
+
                     // Map and copy new segments
                     for segment in elf.segments() {
                         if segment.p_type == program_header::PT_LOAD {