diff --git a/Cargo.toml b/Cargo.toml
index 9817d48f4a1d0d04690dec2abffebf44c4d7fa4e..097d7d6f859779941cc64e5feca38bd4a6e70be9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ crate-type = ["staticlib"]
 [dependencies]
 bitflags = "*"
 spin = "*"
+syscall = { path = "syscall/" }
 
 [dependencies.goblin]
 git = "https://github.com/m4b/goblin.git"
diff --git a/Makefile b/Makefile
index 3c9c5e06c13ad9bcc308a07273a7ab0002f56fd5..6bd76aa956c7923c6632a4a67069a0665cbf629d 100644
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,9 @@ clean:
 	cargo clean --manifest-path libstd/Cargo.toml
 	cargo clean --manifest-path init/Cargo.toml
 	cargo clean --manifest-path ion/Cargo.toml
+	cargo clean --manifest-path drivers/ps2d/Cargo.toml
 	cargo clean --manifest-path drivers/pcid/Cargo.toml
+	cargo clean --manifest-path schemes/example/Cargo.toml
 	rm -rf build
 
 FORCE:
@@ -133,4 +135,12 @@ $(BUILD)/pcid: drivers/pcid/Cargo.toml drivers/pcid/src/** $(BUILD)/libstd.rlib
 	$(CARGO) rustc --manifest-path $< $(CARGOFLAGS) -o $@
 	strip $@
 
-$(BUILD)/initfs.rs: $(BUILD)/init $(BUILD)/ion $(BUILD)/pcid
+$(BUILD)/ps2d: drivers/ps2d/Cargo.toml drivers/ps2d/src/** $(BUILD)/libstd.rlib
+	$(CARGO) rustc --manifest-path $< $(CARGOFLAGS) -o $@
+	strip $@
+
+$(BUILD)/example: schemes/example/Cargo.toml schemes/example/src/** $(BUILD)/libstd.rlib
+	$(CARGO) rustc --manifest-path $< $(CARGOFLAGS) -o $@
+	strip $@
+
+$(BUILD)/initfs.rs: $(BUILD)/init $(BUILD)/ion $(BUILD)/pcid $(BUILD)/ps2d $(BUILD)/example
diff --git a/arch/x86_64/Cargo.toml b/arch/x86_64/Cargo.toml
index 65a644a3d0f1b1944857c183d29d7a7623102e47..567b412ce781d6b0ce379af82657c726bd703d60 100644
--- a/arch/x86_64/Cargo.toml
+++ b/arch/x86_64/Cargo.toml
@@ -4,7 +4,8 @@ version = "0.1.0"
 
 [dependencies]
 bitflags = "*"
-hole_list_allocator = { path = "../../alloc/hole_list_allocator"}
+hole_list_allocator = { path = "../../alloc/hole_list_allocator" }
+io = { path = "../../drivers/io" }
 ransid = { git = "https://github.com/redox-os/ransid.git", branch = "new_api" }
 spin = "*"
 
diff --git a/arch/x86_64/src/acpi/mod.rs b/arch/x86_64/src/acpi/mod.rs
index eccd1650884716d05a1e81e7d031871e12e379ba..f44bd07ffe7ccfe443414536b13d480ef37dafee 100644
--- a/arch/x86_64/src/acpi/mod.rs
+++ b/arch/x86_64/src/acpi/mod.rs
@@ -59,11 +59,11 @@ pub fn init_sdt(sdt: &'static Sdt, active_table: &mut ActivePageTable) {
                     if ap_local_apic.flags & 1 == 1 {
                         // Allocate a stack
                         // TODO: Allocate contiguous
-                        let stack_start = allocate_frame().expect("no more frames in acpi stack_start").start_address().get();
+                        let stack_start = allocate_frame().expect("no more frames in acpi stack_start").start_address().get() + ::KERNEL_OFFSET;
                         for _i in 0..62 {
                             allocate_frame().expect("no more frames in acpi stack");
                         }
-                        let stack_end = allocate_frame().expect("no more frames in acpi stack_end").start_address().get() + 4096;
+                        let stack_end = allocate_frame().expect("no more frames in acpi stack_end").start_address().get() + 4096 + ::KERNEL_OFFSET;
 
                         let ap_ready = TRAMPOLINE as *mut u64;
                         let ap_cpu_id = unsafe { ap_ready.offset(1) };
diff --git a/arch/x86_64/src/context.rs b/arch/x86_64/src/context.rs
index 004914d838c4e3dbfb278209343c3b4b21517869..946330afa5ffed8a8ff84853762cc8b12934d5a9 100644
--- a/arch/x86_64/src/context.rs
+++ b/arch/x86_64/src/context.rs
@@ -43,6 +43,10 @@ impl Context {
         }
     }
 
+    pub fn get_page_table(&self) -> usize {
+        self.cr3
+    }
+
     pub fn set_page_table(&mut self, address: usize) {
         self.cr3 = address;
     }
@@ -52,6 +56,7 @@ impl Context {
     }
 
     /// Switch to the next context by restoring its stack and registers
+    #[cold]
     #[inline(never)]
     #[naked]
     pub unsafe fn switch_to(&mut self, next: &mut Context) {
@@ -94,7 +99,12 @@ impl Context {
         asm!("mov $0, rbp" : "=r"(self.rbp) : : "memory" : "intel", "volatile");
         asm!("mov rbp, $0" : : "r"(next.rbp) : "memory" : "intel", "volatile");
 
-        // Unset global lock, set inside of kernel
-        CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
+        asm!("call context_switch_unlock" : : : "memory" : "intel", "volatile");
     }
 }
+
+/// Unset global lock, set inside of kernel
+#[no_mangle]
+extern fn context_switch_unlock(){
+    CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
+}
diff --git a/arch/x86_64/src/device/mod.rs b/arch/x86_64/src/device/mod.rs
index 02fe70dd11e69d00b70628e0b56fd8c866a2aec9..c8032646e51d355f792a25b3b5f4da2b6f1ccc63 100644
--- a/arch/x86_64/src/device/mod.rs
+++ b/arch/x86_64/src/device/mod.rs
@@ -1,11 +1,9 @@
 use paging::ActivePageTable;
 
 pub mod display;
-pub mod ps2;
 pub mod serial;
 
 pub unsafe fn init(active_table: &mut ActivePageTable){
     serial::init();
     display::init(active_table);
-    ps2::init();
 }
diff --git a/arch/x86_64/src/interrupt/irq.rs b/arch/x86_64/src/interrupt/irq.rs
index a36d01990ed7572ef03fc0c2aad94f6aa2a695b4..091a2e281e41bc3a6bdc78eeb3d79e9b50f4baa4 100644
--- a/arch/x86_64/src/interrupt/irq.rs
+++ b/arch/x86_64/src/interrupt/irq.rs
@@ -1,9 +1,9 @@
 use spin::Mutex;
 use x86::io;
 
-use device::ps2::{PS2_KEYBOARD, PS2_MOUSE};
 use device::serial::{COM1, COM2};
 
+pub static ACKS: Mutex<[usize; 16]> = Mutex::new([0; 16]);
 pub static COUNTS: Mutex<[usize; 16]> = Mutex::new([0; 16]);
 
 #[inline(always)]
@@ -17,6 +17,14 @@ unsafe fn slave_ack() {
     master_ack();
 }
 
+pub unsafe fn acknowledge(irq: usize) {
+    if irq >= 8 {
+        slave_ack();
+    } else {
+        master_ack();
+    }
+}
+
 interrupt!(pit, {
     COUNTS.lock()[0] += 1;
     master_ack();
@@ -24,10 +32,6 @@ interrupt!(pit, {
 
 interrupt!(keyboard, {
     COUNTS.lock()[1] += 1;
-    if let Some(ref mut keyboard) = *PS2_KEYBOARD.lock(){
-        keyboard.on_irq();
-    }
-    master_ack();
 });
 
 interrupt!(cascade, {
@@ -84,10 +88,6 @@ interrupt!(pci3, {
 
 interrupt!(mouse, {
     COUNTS.lock()[12] += 1;
-    if let Some(ref mut mouse) = *PS2_MOUSE.lock() {
-        mouse.on_irq();
-    }
-    slave_ack();
 });
 
 interrupt!(fpu, {
diff --git a/arch/x86_64/src/io/mod.rs b/arch/x86_64/src/io/mod.rs
deleted file mode 100644
index 86a1c34f42c2c4e612000fbbd6adfdc830887efb..0000000000000000000000000000000000000000
--- a/arch/x86_64/src/io/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-/// I/O functions
-
-pub use self::io::*;
-pub use self::mmio::*;
-pub use self::pio::*;
-
-mod io;
-mod mmio;
-mod pio;
diff --git a/arch/x86_64/src/lib.rs b/arch/x86_64/src/lib.rs
index 870bf7384a2b45e5ab7297163450a90f967fadf6..009f72201c983dfb9e1bbdfacc0068b7e6d79806 100644
--- a/arch/x86_64/src/lib.rs
+++ b/arch/x86_64/src/lib.rs
@@ -15,6 +15,7 @@ extern crate hole_list_allocator as allocator;
 
 #[macro_use]
 extern crate bitflags;
+extern crate io;
 extern crate ransid;
 extern crate spin;
 pub extern crate x86;
@@ -58,6 +59,9 @@ pub extern crate x86;
     /// Size of user stack
     pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB
 
+    /// Offset to user grants
+    pub const USER_GRANT_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
+
     /// Offset to user temporary image (used when cloning)
     pub const USER_TMP_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
 
@@ -67,14 +71,16 @@ pub extern crate x86;
     /// Offset to user temporary stack (used when cloning)
     pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE;
 
+    /// Offset to user temporary page for grants
+    pub const USER_TMP_GRANT_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
+
 
 /// Print to console
 #[macro_export]
 macro_rules! print {
     ($($arg:tt)*) => ({
         use core::fmt::Write;
-        let mut console = $crate::console::CONSOLE.lock();
-        let _ = write!(console, $($arg)*);
+        let _ = write!($crate::console::CONSOLE.lock(), $($arg)*);
     });
 }
 
@@ -199,9 +205,6 @@ pub mod gdt;
 /// Interrupt descriptor table
 pub mod idt;
 
-/// IO Handling
-pub mod io;
-
 /// Interrupt instructions
 pub mod interrupt;
 
diff --git a/arch/x86_64/src/paging/mapper.rs b/arch/x86_64/src/paging/mapper.rs
index 5b7665a3f8a329d72a47dbe833fcf77e8980e15e..819efa9b35bb25833ae443c1dd3955150c58de70 100644
--- a/arch/x86_64/src/paging/mapper.rs
+++ b/arch/x86_64/src/paging/mapper.rs
@@ -93,6 +93,13 @@ impl Mapper {
             .and_then(|p1| p1[page.p1_index()].pointed_frame())
     }
 
+    pub fn translate_page_flags(&self, page: Page) -> Option<EntryFlags> {
+        self.p4().next_table(page.p4_index())
+            .and_then(|p3| p3.next_table(page.p3_index()))
+            .and_then(|p2| p2.next_table(page.p2_index()))
+            .and_then(|p1| Some(p1[page.p1_index()].flags()))
+    }
+
     /// Translate a virtual address to a physical one
     pub fn translate(&self, virtual_address: VirtualAddress) -> Option<PhysicalAddress> {
         let offset = virtual_address.get() % PAGE_SIZE;
diff --git a/arch/x86_64/src/paging/mod.rs b/arch/x86_64/src/paging/mod.rs
index 4c3da06016ea1f3ae47200bff057d78cc1b41317..5ebfe11d926be656d26820ff4a4bdd4251589837 100644
--- a/arch/x86_64/src/paging/mod.rs
+++ b/arch/x86_64/src/paging/mod.rs
@@ -133,23 +133,23 @@ pub unsafe fn init(cpu_id: usize, stack_start: usize, stack_end: usize) -> (Acti
                 }
             }
 
-            let mut remap = |start: usize, end: usize, flags: EntryFlags, offset: usize| {
+            let mut remap = |start: usize, end: usize, flags: EntryFlags| {
                 if end > start {
                     let start_frame = Frame::containing_address(PhysicalAddress::new(start));
                     let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1));
                     for frame in Frame::range_inclusive(start_frame, end_frame) {
-                        let page = Page::containing_address(VirtualAddress::new(frame.start_address().get() + offset));
+                        let page = Page::containing_address(VirtualAddress::new(frame.start_address().get() + ::KERNEL_OFFSET));
                         mapper.map_to(page, frame, flags);
                     }
                 }
             };
 
             // Remap stack writable, no execute
-            remap(stack_start, stack_end, PRESENT | NO_EXECUTE | WRITABLE, 0);
+            remap(stack_start - ::KERNEL_OFFSET, stack_end - ::KERNEL_OFFSET, PRESENT | NO_EXECUTE | WRITABLE);
 
             // Remap a section with `flags`
             let mut remap_section = |start: &u8, end: &u8, flags: EntryFlags| {
-                remap(start as *const _ as usize - ::KERNEL_OFFSET, end as *const _ as usize - ::KERNEL_OFFSET, flags, ::KERNEL_OFFSET);
+                remap(start as *const _ as usize - ::KERNEL_OFFSET, end as *const _ as usize - ::KERNEL_OFFSET, flags);
             };
             // Remap text read-only
             remap_section(& __text_start, & __text_end, PRESENT);
@@ -211,19 +211,19 @@ pub unsafe fn init_ap(cpu_id: usize, stack_start: usize, stack_end: usize, kerne
             }
         }
 
-        let mut remap = |start: usize, end: usize, flags: EntryFlags, offset: usize| {
+        let mut remap = |start: usize, end: usize, flags: EntryFlags| {
             if end > start {
                 let start_frame = Frame::containing_address(PhysicalAddress::new(start));
                 let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1));
                 for frame in Frame::range_inclusive(start_frame, end_frame) {
-                    let page = Page::containing_address(VirtualAddress::new(frame.start_address().get() + offset));
+                    let page = Page::containing_address(VirtualAddress::new(frame.start_address().get() + ::KERNEL_OFFSET));
                     mapper.map_to(page, frame, flags);
                 }
             }
         };
 
         // Remap stack writable, no execute
-        remap(stack_start, stack_end, PRESENT | NO_EXECUTE | WRITABLE, 0);
+        remap(stack_start - ::KERNEL_OFFSET, stack_end - ::KERNEL_OFFSET, PRESENT | NO_EXECUTE | WRITABLE);
     });
 
     active_table.switch(new_table);
@@ -328,6 +328,10 @@ impl InactivePageTable {
         InactivePageTable { p4_frame: frame }
     }
 
+    pub unsafe fn from_address(cr3: usize) -> InactivePageTable {
+        InactivePageTable { p4_frame: Frame::containing_address(PhysicalAddress::new(cr3)) }
+    }
+
     pub unsafe fn address(&self) -> usize {
         self.p4_frame.start_address().get()
     }
diff --git a/arch/x86_64/src/start.rs b/arch/x86_64/src/start.rs
index c941a883d1c97bf35d4b5ef4195f20e3459b72d4..10f53c56008f9982167fe837d92922d1e49dbfeb 100644
--- a/arch/x86_64/src/start.rs
+++ b/arch/x86_64/src/start.rs
@@ -12,8 +12,8 @@ use externs::memset;
 use gdt;
 use idt;
 use interrupt;
-use memory::{self, Frame};
-use paging::{self, entry, Page, PhysicalAddress, VirtualAddress};
+use memory;
+use paging::{self, entry, Page, VirtualAddress};
 
 /// Test of zero values in BSS.
 static BSS_TEST_ZERO: usize = 0;
@@ -68,8 +68,8 @@ pub unsafe extern fn kstart() -> ! {
         memory::init(0, &__end as *const u8 as usize - ::KERNEL_OFFSET);
 
         // TODO: allocate a stack
-        let stack_start = 0x00080000;
-        let stack_end = 0x0009F000;
+        let stack_start = 0x00080000 + ::KERNEL_OFFSET;
+        let stack_end = 0x0009F000 + ::KERNEL_OFFSET;
 
         // Initialize paging
         let (mut active_table, tcb_offset) = paging::init(0, stack_start, stack_end);
@@ -148,7 +148,7 @@ pub unsafe extern fn kstart_ap(cpu_id: usize, page_table: usize, stack_start: us
         let kernel_table = KERNEL_TABLE.load(Ordering::SeqCst);
 
         // Initialize paging
-        let (mut active_table, tcb_offset) = paging::init_ap(cpu_id, stack_start, stack_end, kernel_table);
+        let (active_table, tcb_offset) = paging::init_ap(cpu_id, stack_start, stack_end, kernel_table);
 
         // Set up GDT for AP
         gdt::init(tcb_offset, stack_end);
diff --git a/bootloader/x86_64/startup-x86_64.asm b/bootloader/x86_64/startup-x86_64.asm
index 9cb683b520856b3bbb10dd74993b70b561159b38..48d7980f2efe1de3f52f272e55297c01f1f4074f 100644
--- a/bootloader/x86_64/startup-x86_64.asm
+++ b/bootloader/x86_64/startup-x86_64.asm
@@ -122,10 +122,11 @@ long_mode:
     mov gs, rax
     mov ss, rax
 
-    mov rsp, 0x0009F000
+    mov rsp, 0xFFFFFF000009F000
 
     ;rust init
     mov rax, [kernel_base + 0x18]
+    xchg bx, bx
     jmp rax
 
 long_mode_ap:
diff --git a/drivers/io/Cargo.toml b/drivers/io/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee40b297428b67e55088d62657e3efc2e6123e
--- /dev/null
+++ b/drivers/io/Cargo.toml
@@ -0,0 +1,3 @@
+[package]
+name = "io"
+version = "0.1.0"
diff --git a/arch/x86_64/src/io/io.rs b/drivers/io/src/io.rs
similarity index 100%
rename from arch/x86_64/src/io/io.rs
rename to drivers/io/src/io.rs
diff --git a/drivers/io/src/lib.rs b/drivers/io/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..22f8eb72c0ba52ae3bd36795116a98f1fc1cee31
--- /dev/null
+++ b/drivers/io/src/lib.rs
@@ -0,0 +1,14 @@
+//! I/O functions
+
+#![feature(asm)]
+#![feature(const_fn)]
+#![feature(core_intrinsics)]
+#![no_std]
+
+pub use self::io::*;
+pub use self::mmio::*;
+pub use self::pio::*;
+
+mod io;
+mod mmio;
+mod pio;
diff --git a/arch/x86_64/src/io/mmio.rs b/drivers/io/src/mmio.rs
similarity index 100%
rename from arch/x86_64/src/io/mmio.rs
rename to drivers/io/src/mmio.rs
diff --git a/arch/x86_64/src/io/pio.rs b/drivers/io/src/pio.rs
similarity index 53%
rename from arch/x86_64/src/io/pio.rs
rename to drivers/io/src/pio.rs
index 562c1c1e13e3000d9f5a9adb648f36b67d49b5c1..91ae310b624a06029cd872905ea2f97abdc42e9a 100644
--- a/arch/x86_64/src/io/pio.rs
+++ b/drivers/io/src/pio.rs
@@ -1,5 +1,4 @@
 use core::marker::PhantomData;
-use x86::io;
 
 use super::io::Io;
 
@@ -27,13 +26,19 @@ impl Io for Pio<u8> {
     /// Read
     #[inline(always)]
     fn read(&self) -> u8 {
-        unsafe { io::inb(self.port) }
+        let value: u8;
+        unsafe {
+            asm!("in $0, $1" : "={al}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile");
+        }
+        value
     }
 
     /// Write
     #[inline(always)]
     fn write(&mut self, value: u8) {
-        unsafe { io::outb(self.port, value) }
+        unsafe {
+            asm!("out $1, $0" : : "{al}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile");
+        }
     }
 }
 
@@ -44,13 +49,19 @@ impl Io for Pio<u16> {
     /// Read
     #[inline(always)]
     fn read(&self) -> u16 {
-        unsafe { io::inw(self.port) }
+        let value: u16;
+        unsafe {
+            asm!("in $0, $1" : "={ax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile");
+        }
+        value
     }
 
     /// Write
     #[inline(always)]
     fn write(&mut self, value: u16) {
-        unsafe { io::outw(self.port, value) }
+        unsafe {
+            asm!("out $1, $0" : : "{ax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile");
+        }
     }
 }
 
@@ -61,12 +72,18 @@ impl Io for Pio<u32> {
     /// Read
     #[inline(always)]
     fn read(&self) -> u32 {
-        unsafe { io::inl(self.port) }
+        let value: u32;
+        unsafe {
+            asm!("in $0, $1" : "={eax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile");
+        }
+        value
     }
 
     /// Write
     #[inline(always)]
     fn write(&mut self, value: u32) {
-        unsafe { io::outl(self.port, value) }
+        unsafe {
+            asm!("out $1, $0" : : "{eax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile");
+        }
     }
 }
diff --git a/drivers/ps2d/Cargo.toml b/drivers/ps2d/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..665e3c19c89c0898c0fd97330cf3843b86519453
--- /dev/null
+++ b/drivers/ps2d/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "ps2d"
+version = "0.1.0"
+
+[dependencies]
+bitflags = "*"
+io = { path = "../io/" }
+spin = "*"
+syscall = { path = "../../syscall/" }
diff --git a/arch/x86_64/src/device/ps2.rs b/drivers/ps2d/src/controller.rs
similarity index 59%
rename from arch/x86_64/src/device/ps2.rs
rename to drivers/ps2d/src/controller.rs
index 898008659e030b37f291624ff12da8e6c52b7ab4..e68d4f13ecfe9cfff62a8c4fd9def38f0f3e6523 100644
--- a/arch/x86_64/src/device/ps2.rs
+++ b/drivers/ps2d/src/controller.rs
@@ -1,17 +1,5 @@
-use core::cmp;
-use spin::Mutex;
-
 use io::{Io, Pio, ReadOnly, WriteOnly};
 
-pub static PS2_KEYBOARD: Mutex<Option<Ps2Keyboard>> = Mutex::new(None);
-pub static PS2_MOUSE: Mutex<Option<Ps2Mouse>> = Mutex::new(None);
-
-pub unsafe fn init() {
-    let (keyboard, mouse) = Ps2::new().init();
-    *PS2_KEYBOARD.lock() = keyboard;
-    *PS2_MOUSE.lock() = mouse;
-}
-
 bitflags! {
     flags StatusFlags: u8 {
         const OUTPUT_FULL = 1,
@@ -85,114 +73,6 @@ enum MouseCommandData {
     SetSampleRate = 0xF3,
 }
 
-bitflags! {
-    flags MousePacketFlags: u8 {
-        const LEFT_BUTTON = 1,
-        const RIGHT_BUTTON = 1 << 1,
-        const MIDDLE_BUTTON = 1 << 2,
-        const ALWAYS_ON = 1 << 3,
-        const X_SIGN = 1 << 4,
-        const Y_SIGN = 1 << 5,
-        const X_OVERFLOW = 1 << 6,
-        const Y_OVERFLOW = 1 << 7
-    }
-}
-
-pub struct Ps2Keyboard {
-    data: ReadOnly<Pio<u8>>,
-    key: [u8; 3],
-    key_i: usize,
-}
-
-impl Ps2Keyboard {
-    fn new() -> Self {
-        Ps2Keyboard {
-            data: ReadOnly::new(Pio::new(0x60)),
-            key: [0; 3],
-            key_i: 0
-        }
-    }
-
-    pub fn on_irq(&mut self) {
-        let scancode = self.data.read();
-        self.key[self.key_i] = scancode;
-        self.key_i += 1;
-        if self.key_i >= self.key.len() || scancode < 0xE0 {
-            println!("KEY: {:X} {:X} {:X}", self.key[0], self.key[1], self.key[2]);
-
-            self.key = [0; 3];
-            self.key_i = 0;
-        }
-    }
-}
-
-pub struct Ps2Mouse {
-    data: ReadOnly<Pio<u8>>,
-    mouse: [u8; 4],
-    mouse_i: usize,
-    mouse_extra: bool,
-    mouse_x: usize,
-    mouse_y: usize
-}
-
-impl Ps2Mouse {
-    fn new(mouse_extra: bool) -> Self {
-        Ps2Mouse {
-            data: ReadOnly::new(Pio::new(0x60)),
-            mouse: [0; 4],
-            mouse_i: 0,
-            mouse_extra: mouse_extra,
-            mouse_x: 0,
-            mouse_y: 0
-        }
-    }
-
-    pub fn on_irq(&mut self) {
-        self.mouse[self.mouse_i] = self.data.read();
-        self.mouse_i += 1;
-
-        let flags = MousePacketFlags::from_bits_truncate(self.mouse[0]);
-        if ! flags.contains(ALWAYS_ON) {
-            println!("MOUSE MISALIGN {:X}", self.mouse[0]);
-
-            self.mouse = [0; 4];
-            self.mouse_i = 0;
-        } else if self.mouse_i >= self.mouse.len() || (!self.mouse_extra && self.mouse_i >= 3) {
-            if ! flags.contains(X_OVERFLOW) && ! flags.contains(Y_OVERFLOW) {
-                let mut dx = self.mouse[1] as isize;
-                if flags.contains(X_SIGN) {
-                    dx -= 0x100;
-                }
-
-                let mut dy = self.mouse[2] as isize;
-                if flags.contains(Y_SIGN) {
-                    dy -= 0x100;
-                }
-
-                let _extra = if self.mouse_extra {
-                    self.mouse[3]
-                } else {
-                    0
-                };
-
-                //print!("MOUSE {:?}, {}, {}, {}\n", flags, dx, dy, extra);
-
-                if let Some(ref mut display) = *super::display::DISPLAY.lock() {
-                    self.mouse_x = cmp::max(0, cmp::min(display.width as isize - 1, self.mouse_x as isize + dx)) as usize;
-                    self.mouse_y = cmp::max(0, cmp::min(display.height as isize - 1, self.mouse_y as isize - dy)) as usize;
-                    let offset = self.mouse_y * display.width + self.mouse_x;
-                    display.onscreen[offset as usize] = 0xFF0000;
-                }
-            } else {
-                println!("MOUSE OVERFLOW {:X} {:X} {:X} {:X}", self.mouse[0], self.mouse[1], self.mouse[2], self.mouse[3]);
-            }
-
-            self.mouse = [0; 4];
-            self.mouse_i = 0;
-        }
-    }
-}
-
 pub struct Ps2 {
     data: Pio<u8>,
     status: ReadOnly<Pio<u8>>,
@@ -200,7 +80,7 @@ pub struct Ps2 {
 }
 
 impl Ps2 {
-    const fn new() -> Self {
+    pub fn new() -> Self {
         Ps2 {
             data: Pio::new(0x60),
             status: ReadOnly::new(Pio::new(0x64)),
@@ -278,7 +158,7 @@ impl Ps2 {
         self.read()
     }
 
-    fn init(&mut self) -> (Option<Ps2Keyboard>, Option<Ps2Mouse>) {
+    pub fn init(&mut self) -> bool {
         // Disable devices
         self.command(Command::DisableFirst);
         self.command(Command::DisableSecond);
@@ -328,18 +208,6 @@ impl Ps2 {
         let mouse_id = self.read();
         let mouse_extra = mouse_id == 3;
 
-        // Enable extra buttons, TODO
-        /*
-        if self.mouse_extra {
-            print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));
-            print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));
-            print!("SAMPLE 80 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 80));
-            print!("GET ID {:X}\n", self.mouse_command(MouseCommand::GetDeviceId));
-            let mouse_id = self.read();
-            print!("MOUSE ID: {:X} == 0x04\n", mouse_id);
-        }
-        */
-
         // Set sample rate to maximum
         assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 200), 0xFA);
 
@@ -352,6 +220,7 @@ impl Ps2 {
             let mut config = self.config();
             config.remove(FIRST_DISABLED);
             config.remove(SECOND_DISABLED);
+            config.insert(FIRST_TRANSLATE);
             config.insert(FIRST_INTERRUPT);
             config.insert(SECOND_INTERRUPT);
             self.set_config(config);
@@ -359,6 +228,6 @@ impl Ps2 {
 
         self.flush_read();
 
-        (Some(Ps2Keyboard::new()), Some(Ps2Mouse::new(mouse_extra)))
+        mouse_extra
     }
 }
diff --git a/drivers/ps2d/src/keyboard.rs b/drivers/ps2d/src/keyboard.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b0090a3ad08da7cfdcef0e3a37848c3095b48cf8
--- /dev/null
+++ b/drivers/ps2d/src/keyboard.rs
@@ -0,0 +1,34 @@
+use std::fs::File;
+use std::io::{Read, Write};
+use std::mem;
+use std::thread;
+
+use keymap;
+
+pub fn keyboard() {    
+    let mut file = File::open("irq:1").expect("ps2d: failed to open irq:1");
+
+    loop {
+        let mut irqs = [0; 8];
+        if file.read(&mut irqs).expect("ps2d: failed to read irq:1") >= mem::size_of::<usize>() {
+            let data: u8;
+            unsafe {
+                asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile");
+            }
+
+            let (scancode, pressed) = if data >= 0x80 {
+                (data - 0x80, false)
+            } else {
+                (data, true)
+            };
+
+            if pressed {
+                print!("{}", keymap::get_char(scancode));
+            }
+
+            file.write(&irqs).expect("ps2d: failed to write irq:1");
+        } else {
+            thread::yield_now();
+        }
+    }
+}
diff --git a/drivers/ps2d/src/keymap.rs b/drivers/ps2d/src/keymap.rs
new file mode 100644
index 0000000000000000000000000000000000000000..699e3ad1445c932f032be6d570eb279fc4246365
--- /dev/null
+++ b/drivers/ps2d/src/keymap.rs
@@ -0,0 +1,68 @@
+static ENGLISH: [[char; 3]; 58] = [
+    ['\0', '\0', '\0'],
+    ['\x1B', '\x1B', '\x1B'],
+    ['1', '!', '1'],
+    ['2', '@', '2'],
+    ['3', '#', '3'],
+    ['4', '$', '4'],
+    ['5', '%', '5'],
+    ['6', '^', '6'],
+    ['7', '&', '7'],
+    ['8', '*', '8'],
+    ['9', '(', '9'],
+    ['0', ')', '0'],
+    ['-', '_', '-'],
+    ['=', '+', '='],
+    ['\0', '\0', '\0'],
+    ['\t', '\t', '\t'],
+    ['q', 'Q', 'q'],
+    ['w', 'W', 'w'],
+    ['e', 'E', 'e'],
+    ['r', 'R', 'r'],
+    ['t', 'T', 't'],
+    ['y', 'Y', 'y'],
+    ['u', 'U', 'u'],
+    ['i', 'I', 'i'],
+    ['o', 'O', 'o'],
+    ['p', 'P', 'p'],
+    ['[', '{', '['],
+    [']', '}', ']'],
+    ['\n', '\n', '\n'],
+    ['\0', '\0', '\0'],
+    ['a', 'A', 'a'],
+    ['s', 'S', 's'],
+    ['d', 'D', 'd'],
+    ['f', 'F', 'f'],
+    ['g', 'G', 'g'],
+    ['h', 'H', 'h'],
+    ['j', 'J', 'j'],
+    ['k', 'K', 'k'],
+    ['l', 'L', 'l'],
+    [';', ':', ';'],
+    ['\'', '"', '\''],
+    ['`', '~', '`'],
+    ['\0', '\0', '\0'],
+    ['\\', '|', '\\'],
+    ['z', 'Z', 'z'],
+    ['x', 'X', 'x'],
+    ['c', 'C', 'c'],
+    ['v', 'V', 'v'],
+    ['b', 'B', 'b'],
+    ['n', 'N', 'n'],
+    ['m', 'M', 'm'],
+    [',', '<', ','],
+    ['.', '>', '.'],
+    ['/', '?', '/'],
+    ['\0', '\0', '\0'],
+    ['\0', '\0', '\0'],
+    ['\0', '\0', '\0'],
+    [' ', ' ', ' ']
+];
+
+pub fn get_char(scancode: u8) -> char {
+    if let Some(c) = ENGLISH.get(scancode as usize) {
+        c[0]
+    } else {
+        '\0'
+    }
+}
diff --git a/drivers/ps2d/src/main.rs b/drivers/ps2d/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4a3888eab85c5b1c96d952598079a9a4b29c19ad
--- /dev/null
+++ b/drivers/ps2d/src/main.rs
@@ -0,0 +1,42 @@
+#![feature(asm)]
+
+#[macro_use]
+extern crate bitflags;
+extern crate io;
+extern crate syscall;
+
+use std::thread;
+
+use syscall::iopl;
+
+mod controller;
+mod keyboard;
+mod keymap;
+mod mouse;
+
+fn main() {
+    unsafe {
+        iopl(3).expect("ps2d: failed to get I/O permission");
+        asm!("cli" :::: "intel", "volatile");
+    }
+
+    let extra_packet = controller::Ps2::new().init();
+
+    thread::spawn(|| {
+        unsafe {
+            iopl(3).expect("ps2d: failed to get I/O permission");
+            asm!("cli" :::: "intel", "volatile");
+        }
+
+        keyboard::keyboard();
+    });
+
+    thread::spawn(move || {
+        unsafe {
+            iopl(3).expect("ps2d: failed to get I/O permission");
+            asm!("cli" :::: "intel", "volatile");
+        }
+
+        mouse::mouse(extra_packet);
+    });
+}
diff --git a/drivers/ps2d/src/mouse.rs b/drivers/ps2d/src/mouse.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f273de9670ce2612284141ca0d9f32638c9188cd
--- /dev/null
+++ b/drivers/ps2d/src/mouse.rs
@@ -0,0 +1,73 @@
+use std::fs::File;
+use std::io::{Read, Write};
+use std::mem;
+use std::thread;
+
+bitflags! {
+    flags MousePacketFlags: u8 {
+        const LEFT_BUTTON = 1,
+        const RIGHT_BUTTON = 1 << 1,
+        const MIDDLE_BUTTON = 1 << 2,
+        const ALWAYS_ON = 1 << 3,
+        const X_SIGN = 1 << 4,
+        const Y_SIGN = 1 << 5,
+        const X_OVERFLOW = 1 << 6,
+        const Y_OVERFLOW = 1 << 7
+    }
+}
+
+pub fn mouse(extra_packet: bool) {
+    let mut file = File::open("irq:12").expect("ps2d: failed to open irq:12");
+
+    let mut packets = [0; 4];
+    let mut packet_i = 0;
+    loop {
+        let mut irqs = [0; 8];
+        if file.read(&mut irqs).expect("ps2d: failed to read irq:12") >= mem::size_of::<usize>() {
+            let data: u8;
+            unsafe {
+                asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile");
+            }
+
+            packets[packet_i] = data;
+            packet_i += 1;
+
+            let flags = MousePacketFlags::from_bits_truncate(packets[0]);
+            if ! flags.contains(ALWAYS_ON) {
+                println!("MOUSE MISALIGN {:X}", packets[0]);
+
+                packets = [0; 4];
+                packet_i = 0;
+            } else if packet_i >= packets.len() || (!extra_packet && packet_i >= 3) {
+                if ! flags.contains(X_OVERFLOW) && ! flags.contains(Y_OVERFLOW) {
+                    let mut dx = packets[1] as isize;
+                    if flags.contains(X_SIGN) {
+                        dx -= 0x100;
+                    }
+
+                    let mut dy = packets[2] as isize;
+                    if flags.contains(Y_SIGN) {
+                        dy -= 0x100;
+                    }
+
+                    let extra = if extra_packet {
+                        packets[3]
+                    } else {
+                        0
+                    };
+
+                    print!("ps2d: IRQ {:?}, {}, {}, {}\n", flags, dx, dy, extra);
+                } else {
+                    println!("ps2d: overflow {:X} {:X} {:X} {:X}", packets[0], packets[1], packets[2], packets[3]);
+                }
+
+                packets = [0; 4];
+                packet_i = 0;
+            }
+
+            file.write(&irqs).expect("ps2d: failed to write irq:12");
+        } else {
+            thread::yield_now();
+        }
+    }
+}
diff --git a/kernel/context/context.rs b/kernel/context/context.rs
index 4b4cc3a7d568b176153a3c5f8189561d193ba1fa..73f31f417313f7dee206668d8bd53facccd04804 100644
--- a/kernel/context/context.rs
+++ b/kernel/context/context.rs
@@ -5,7 +5,7 @@ use spin::Mutex;
 
 use arch;
 use super::file::File;
-use super::memory::{Memory, SharedMemory};
+use super::memory::{Grant, Memory, SharedMemory};
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum Status {
@@ -33,6 +33,8 @@ pub struct Context {
     pub heap: Option<SharedMemory>,
     /// User stack
     pub stack: Option<Memory>,
+    /// User grants
+    pub grants: Arc<Mutex<Vec<Grant>>>,
     /// The current working directory
     pub cwd: Arc<Mutex<Vec<u8>>>,
     /// The open files in the scheme
@@ -51,6 +53,7 @@ impl Context {
             image: Vec::new(),
             heap: None,
             stack: None,
+            grants: Arc::new(Mutex::new(Vec::new())),
             cwd: Arc::new(Mutex::new(Vec::new())),
             files: Arc::new(Mutex::new(Vec::new()))
         }
diff --git a/kernel/context/list.rs b/kernel/context/list.rs
index 7e60e767604fa7f394b061446108c9ef62c84151..2ad5efb9a910ce1853de6c6b2b036f51b5f4b6d1 100644
--- a/kernel/context/list.rs
+++ b/kernel/context/list.rs
@@ -1,15 +1,16 @@
+use alloc::arc::Arc;
 use collections::BTreeMap;
 use core::mem;
 use core::sync::atomic::Ordering;
 use spin::RwLock;
 
 use arch;
-use syscall::{Result, Error};
+use syscall::error::{Result, Error, EAGAIN};
 use super::context::Context;
 
 /// Context list type
 pub struct ContextList {
-    map: BTreeMap<usize, RwLock<Context>>,
+    map: BTreeMap<usize, Arc<RwLock<Context>>>,
     next_id: usize
 }
 
@@ -23,21 +24,21 @@ impl ContextList {
     }
 
     /// Get the nth context.
-    pub fn get(&self, id: usize) -> Option<&RwLock<Context>> {
+    pub fn get(&self, id: usize) -> Option<&Arc<RwLock<Context>>> {
         self.map.get(&id)
     }
 
     /// Get the current context.
-    pub fn current(&self) -> Option<&RwLock<Context>> {
+    pub fn current(&self) -> Option<&Arc<RwLock<Context>>> {
         self.map.get(&super::CONTEXT_ID.load(Ordering::SeqCst))
     }
 
-    pub fn iter(&self) -> ::collections::btree_map::Iter<usize, RwLock<Context>> {
+    pub fn iter(&self) -> ::collections::btree_map::Iter<usize, Arc<RwLock<Context>>> {
         self.map.iter()
     }
 
     /// Create a new context.
-    pub fn new_context(&mut self) -> Result<&RwLock<Context>> {
+    pub fn new_context(&mut self) -> Result<&Arc<RwLock<Context>>> {
         if self.next_id >= super::CONTEXT_MAX_CONTEXTS {
             self.next_id = 1;
         }
@@ -47,19 +48,19 @@ impl ContextList {
         }
 
         if self.next_id >= super::CONTEXT_MAX_CONTEXTS {
-            return Err(Error::TryAgain);
+            return Err(Error::new(EAGAIN));
         }
 
         let id = self.next_id;
         self.next_id += 1;
 
-        assert!(self.map.insert(id, RwLock::new(Context::new(id))).is_none());
+        assert!(self.map.insert(id, Arc::new(RwLock::new(Context::new(id)))).is_none());
 
         Ok(self.map.get(&id).expect("Failed to insert new context. ID is out of bounds."))
     }
 
     /// Spawn a context from a function.
-    pub fn spawn(&mut self, func: extern fn()) -> Result<&RwLock<Context>> {
+    pub fn spawn(&mut self, func: extern fn()) -> Result<&Arc<RwLock<Context>>> {
         let context_lock = self.new_context()?;
         {
             let mut context = context_lock.write();
@@ -77,7 +78,7 @@ impl ContextList {
         Ok(context_lock)
     }
 
-    pub fn remove(&mut self, id: usize) -> Option<RwLock<Context>> {
+    pub fn remove(&mut self, id: usize) -> Option<Arc<RwLock<Context>>> {
         self.map.remove(&id)
     }
 }
diff --git a/kernel/context/memory.rs b/kernel/context/memory.rs
index 34e69a3030e5cd0c5db81b50b60e8639d71cb36a..3121794dc5706034e34bc82e03d33d06a1ec9a96 100644
--- a/kernel/context/memory.rs
+++ b/kernel/context/memory.rs
@@ -1,4 +1,5 @@
 use alloc::arc::{Arc, Weak};
+use collections::VecDeque;
 use spin::Mutex;
 
 use arch::externs::memset;
@@ -7,13 +8,67 @@ use arch::paging::entry::{self, EntryFlags};
 use arch::paging::temporary_page::TemporaryPage;
 
 #[derive(Debug)]
-pub struct Memory {
+pub struct Grant {
     start: VirtualAddress,
     size: usize,
     flags: EntryFlags
 }
 
-#[derive(Debug)]
+impl Grant {
+    pub fn new(from: VirtualAddress, to: VirtualAddress, size: usize, flags: EntryFlags, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) -> Grant {
+        let mut active_table = unsafe { ActivePageTable::new() };
+
+        let mut frames = VecDeque::new();
+
+        let start_page = Page::containing_address(from);
+        let end_page = Page::containing_address(VirtualAddress::new(from.get() + size - 1));
+        for page in Page::range_inclusive(start_page, end_page) {
+            let frame = active_table.translate_page(page).expect("grant references unmapped memory");
+            frames.push_back(frame);
+        }
+
+        active_table.with(new_table, temporary_page, |mapper| {
+            let start_page = Page::containing_address(to);
+            let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
+            for page in Page::range_inclusive(start_page, end_page) {
+                let frame = frames.pop_front().expect("grant did not find enough frames");
+                mapper.map_to(page, frame, flags);
+            }
+        });
+
+        Grant {
+            start: to,
+            size: size,
+            flags: flags
+        }
+    }
+
+    pub fn destroy(self, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) {
+        let mut active_table = unsafe { ActivePageTable::new() };
+
+        active_table.with(new_table, temporary_page, |mapper| {
+            let start_page = Page::containing_address(self.start);
+            let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
+            for page in Page::range_inclusive(start_page, end_page) {
+                mapper.unmap_return(page);
+            }
+        });
+    }
+
+    pub fn start_address(&self) -> VirtualAddress {
+        self.start
+    }
+
+    pub fn size(&self) -> usize {
+        self.size
+    }
+
+    pub fn flags(&self) -> EntryFlags {
+        self.flags
+    }
+}
+
+#[derive(Clone, Debug)]
 pub enum SharedMemory {
     Owned(Arc<Mutex<Memory>>),
     Borrowed(Weak<Mutex<Memory>>)
@@ -42,6 +97,13 @@ impl SharedMemory {
     }
 }
 
+#[derive(Debug)]
+pub struct Memory {
+    start: VirtualAddress,
+    size: usize,
+    flags: EntryFlags
+}
+
 impl Memory {
     pub fn new(start: VirtualAddress, size: usize, flags: EntryFlags, flush: bool, clear: bool) -> Self {
         let mut memory = Memory {
diff --git a/kernel/context/switch.rs b/kernel/context/switch.rs
index 49902d274b898636230e4f771e75894272d9fd98..2e16727025cd22af22c65be5b57bfd9123c04971 100644
--- a/kernel/context/switch.rs
+++ b/kernel/context/switch.rs
@@ -54,6 +54,8 @@ pub unsafe fn switch() {
         return;
     }
 
+    //println!("Switch {} to {}", (&*from_ptr).id, (&*to_ptr).id);
+
     (&mut *from_ptr).running = false;
     (&mut *to_ptr).running = true;
     if let Some(ref stack) = (*to_ptr).kstack {
diff --git a/kernel/scheme/debug.rs b/kernel/scheme/debug.rs
index 0dc67267ac9a17831497c37e8b40085b914440b1..70459e702c77f08618dd1f6ea766ee4c08627144 100644
--- a/kernel/scheme/debug.rs
+++ b/kernel/scheme/debug.rs
@@ -3,8 +3,8 @@ use core::str;
 use spin::{Mutex, Once};
 
 use context;
-use syscall::Result;
-use super::Scheme;
+use syscall::error::*;
+use syscall::scheme::Scheme;
 
 /// Input
 static INPUT: Once<Mutex<VecDeque<u8>>> = Once::new();
@@ -23,18 +23,18 @@ pub extern fn debug_input(b: u8) {
 pub struct DebugScheme;
 
 impl Scheme for DebugScheme {
-    fn open(&mut self, _path: &[u8], _flags: usize) -> Result<usize> {
+    fn open(&self, _path: &[u8], _flags: usize) -> Result<usize> {
         Ok(0)
     }
 
-    fn dup(&mut self, _file: usize) -> Result<usize> {
+    fn dup(&self, _file: usize) -> Result<usize> {
         Ok(0)
     }
 
     /// Read the file `number` into the `buffer`
     ///
     /// Returns the number of bytes read
-    fn read(&mut self, _file: usize, buf: &mut [u8]) -> Result<usize> {
+    fn read(&self, _file: usize, buf: &mut [u8]) -> Result<usize> {
         loop {
             let mut i = 0;
             {
@@ -56,18 +56,18 @@ impl Scheme for DebugScheme {
     /// Write the `buffer` to the `file`
     ///
     /// Returns the number of bytes written
-    fn write(&mut self, _file: usize, buffer: &[u8]) -> Result<usize> {
+    fn write(&self, _file: usize, buffer: &[u8]) -> Result<usize> {
         //TODO: Write bytes, do not convert to str
         print!("{}", unsafe { str::from_utf8_unchecked(buffer) });
         Ok(buffer.len())
     }
 
-    fn fsync(&mut self, file: usize) -> Result<()> {
-        Ok(())
+    fn fsync(&self, _file: usize) -> Result<usize> {
+        Ok(0)
     }
 
     /// Close the file `number`
-    fn close(&mut self, _file: usize) -> Result<()> {
-        Ok(())
+    fn close(&self, _file: usize) -> Result<usize> {
+        Ok(0)
     }
 }
diff --git a/kernel/scheme/env.rs b/kernel/scheme/env.rs
index a04142f11e4ba4cff89fd675a0808560c98d11be..3f3f1f567c9667db9134425bfb903400a5df13a1 100644
--- a/kernel/scheme/env.rs
+++ b/kernel/scheme/env.rs
@@ -1,7 +1,11 @@
 use collections::BTreeMap;
+use core::cmp;
+use core::sync::atomic::{AtomicUsize, Ordering};
+use spin::RwLock;
 
-use syscall::{Error, Result};
-use super::Scheme;
+use syscall::error::*;
+use syscall::flag::{SEEK_SET, SEEK_CUR, SEEK_END};
+use syscall::scheme::Scheme;
 
 struct Handle {
     data: &'static [u8],
@@ -9,9 +13,9 @@ struct Handle {
 }
 
 pub struct EnvScheme {
-    next_id: usize,
+    next_id: AtomicUsize,
     files: BTreeMap<&'static [u8], &'static [u8]>,
-    handles: BTreeMap<usize, Handle>
+    handles: RwLock<BTreeMap<usize, Handle>>
 }
 
 impl EnvScheme {
@@ -24,20 +28,19 @@ impl EnvScheme {
         files.insert(b"LINES", b"30");
 
         EnvScheme {
-            next_id: 0,
+            next_id: AtomicUsize::new(0),
             files: files,
-            handles: BTreeMap::new()
+            handles: RwLock::new(BTreeMap::new())
         }
     }
 }
 
 impl Scheme for EnvScheme {
-    fn open(&mut self, path: &[u8], _flags: usize) -> Result<usize> {
-        let data = self.files.get(path).ok_or(Error::NoEntry)?;
+    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+        let data = self.files.get(path).ok_or(Error::new(ENOENT))?;
 
-        let id = self.next_id;
-        self.next_id += 1;
-        self.handles.insert(id, Handle {
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(id, Handle {
             data: data,
             seek: 0
         });
@@ -45,15 +48,15 @@ impl Scheme for EnvScheme {
         Ok(id)
     }
 
-    fn dup(&mut self, file: usize) -> Result<usize> {
+    fn dup(&self, file: usize) -> Result<usize> {
         let (data, seek) = {
-            let handle = self.handles.get(&file).ok_or(Error::BadFile)?;
+            let handles = self.handles.read();
+            let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
             (handle.data, handle.seek)
         };
 
-        let id = self.next_id;
-        self.next_id += 1;
-        self.handles.insert(id, Handle {
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(id, Handle {
             data: data,
             seek: seek
         });
@@ -61,8 +64,9 @@ impl Scheme for EnvScheme {
         Ok(id)
     }
 
-    fn read(&mut self, file: usize, buffer: &mut [u8]) -> Result<usize> {
-        let mut handle = self.handles.get_mut(&file).ok_or(Error::BadFile)?;
+    fn read(&self, file: usize, buffer: &mut [u8]) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let mut handle = handles.get_mut(&file).ok_or(Error::new(EBADF))?;
 
         let mut i = 0;
         while i < buffer.len() && handle.seek < handle.data.len() {
@@ -74,15 +78,25 @@ impl Scheme for EnvScheme {
         Ok(i)
     }
 
-    fn write(&mut self, _file: usize, _buffer: &[u8]) -> Result<usize> {
-        Err(Error::NotPermitted)
+    fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
+
+        handle.seek = match whence {
+            SEEK_SET => cmp::min(handle.data.len(), pos),
+            SEEK_CUR => cmp::max(0, cmp::min(handle.data.len() as isize, handle.seek as isize + pos as isize)) as usize,
+            SEEK_END => cmp::max(0, cmp::min(handle.data.len() as isize, handle.data.len() as isize + pos as isize)) as usize,
+            _ => return Err(Error::new(EINVAL))
+        };
+
+        Ok(handle.seek)
     }
 
-    fn fsync(&mut self, file: usize) -> Result<()> {
-        Ok(())
+    fn fsync(&self, _file: usize) -> Result<usize> {
+        Ok(0)
     }
 
-    fn close(&mut self, file: usize) -> Result<()> {
-        self.handles.remove(&file).ok_or(Error::BadFile).and(Ok(()))
+    fn close(&self, file: usize) -> Result<usize> {
+        self.handles.write().remove(&file).ok_or(Error::new(EBADF)).and(Ok(0))
     }
 }
diff --git a/kernel/scheme/initfs.rs b/kernel/scheme/initfs.rs
index 0b15bd239589060d272f3bd1999123c83b01fafa..84df203dc461eb0c91efd97c57c8b4c439a90ace 100644
--- a/kernel/scheme/initfs.rs
+++ b/kernel/scheme/initfs.rs
@@ -1,7 +1,11 @@
 use collections::BTreeMap;
+use core::cmp;
+use core::sync::atomic::{AtomicUsize, Ordering};
+use spin::RwLock;
 
-use syscall::{Error, Result};
-use super::Scheme;
+use syscall::error::*;
+use syscall::flag::{SEEK_SET, SEEK_CUR, SEEK_END};
+use syscall::scheme::Scheme;
 
 struct Handle {
     data: &'static [u8],
@@ -9,9 +13,9 @@ struct Handle {
 }
 
 pub struct InitFsScheme {
-    next_id: usize,
+    next_id: AtomicUsize,
     files: BTreeMap<&'static [u8], &'static [u8]>,
-    handles: BTreeMap<usize, Handle>
+    handles: RwLock<BTreeMap<usize, Handle>>
 }
 
 impl InitFsScheme {
@@ -21,23 +25,24 @@ impl InitFsScheme {
         files.insert(b"bin/init", include_bytes!("../../build/userspace/init"));
         files.insert(b"bin/ion", include_bytes!("../../build/userspace/ion"));
         files.insert(b"bin/pcid", include_bytes!("../../build/userspace/pcid"));
-        files.insert(b"etc/init.rc", b"echo testing\ninitfs:bin/pcid\ninitfs:bin/ion");
+        files.insert(b"bin/ps2d", include_bytes!("../../build/userspace/ps2d"));
+        files.insert(b"bin/example", include_bytes!("../../build/userspace/example"));
+        files.insert(b"etc/init.rc", b"initfs:bin/pcid\ninitfs:bin/ps2d\ninitfs:bin/example\ninitfs:bin/ion");
 
         InitFsScheme {
-            next_id: 0,
+            next_id: AtomicUsize::new(0),
             files: files,
-            handles: BTreeMap::new()
+            handles: RwLock::new(BTreeMap::new())
         }
     }
 }
 
 impl Scheme for InitFsScheme {
-    fn open(&mut self, path: &[u8], _flags: usize) -> Result<usize> {
-        let data = self.files.get(path).ok_or(Error::NoEntry)?;
+    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+        let data = self.files.get(path).ok_or(Error::new(ENOENT))?;
 
-        let id = self.next_id;
-        self.next_id += 1;
-        self.handles.insert(id, Handle {
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(id, Handle {
             data: data,
             seek: 0
         });
@@ -45,28 +50,29 @@ impl Scheme for InitFsScheme {
         Ok(id)
     }
 
-    fn dup(&mut self, file: usize) -> Result<usize> {
+    fn dup(&self, id: usize) -> Result<usize> {
         let (data, seek) = {
-            let handle = self.handles.get(&file).ok_or(Error::BadFile)?;
+            let handles = self.handles.read();
+            let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
             (handle.data, handle.seek)
         };
 
-        let id = self.next_id;
-        self.next_id += 1;
-        self.handles.insert(id, Handle {
+        let new_id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(new_id, Handle {
             data: data,
             seek: seek
         });
 
-        Ok(id)
+        Ok(new_id)
     }
 
-    fn read(&mut self, file: usize, buffer: &mut [u8]) -> Result<usize> {
-        let mut handle = self.handles.get_mut(&file).ok_or(Error::BadFile)?;
+    fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
 
         let mut i = 0;
-        while i < buffer.len() && handle.seek < handle.data.len() {
-            buffer[i] = handle.data[handle.seek];
+        while i < buf.len() && handle.seek < handle.data.len() {
+            buf[i] = handle.data[handle.seek];
             i += 1;
             handle.seek += 1;
         }
@@ -74,15 +80,25 @@ impl Scheme for InitFsScheme {
         Ok(i)
     }
 
-    fn write(&mut self, _file: usize, _buffer: &[u8]) -> Result<usize> {
-        Err(Error::NotPermitted)
+    fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
+
+        handle.seek = match whence {
+            SEEK_SET => cmp::min(handle.data.len(), pos),
+            SEEK_CUR => cmp::max(0, cmp::min(handle.data.len() as isize, handle.seek as isize + pos as isize)) as usize,
+            SEEK_END => cmp::max(0, cmp::min(handle.data.len() as isize, handle.data.len() as isize + pos as isize)) as usize,
+            _ => return Err(Error::new(EINVAL))
+        };
+
+        Ok(handle.seek)
     }
 
-    fn fsync(&mut self, file: usize) -> Result<()> {
-        Ok(())
+    fn fsync(&self, _id: usize) -> Result<usize> {
+        Ok(0)
     }
 
-    fn close(&mut self, file: usize) -> Result<()> {
-        self.handles.remove(&file).ok_or(Error::BadFile).and(Ok(()))
+    fn close(&self, id: usize) -> Result<usize> {
+        self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
     }
 }
diff --git a/kernel/scheme/irq.rs b/kernel/scheme/irq.rs
index 9d7d42dae7353c3fbef37c8add16139941ff6c2c..7ec202ab88ac4cf32f6eb4f06006b24f79220d35 100644
--- a/kernel/scheme/irq.rs
+++ b/kernel/scheme/irq.rs
@@ -1,57 +1,68 @@
 use core::{mem, str};
 
-use arch::interrupt::irq::COUNTS;
-use context;
-use syscall::{Error, Result};
-use super::Scheme;
+use arch::interrupt::irq::{ACKS, COUNTS, acknowledge};
+use syscall::error::*;
+use syscall::scheme::Scheme;
 
 pub struct IrqScheme;
 
 impl Scheme for IrqScheme {
-    fn open(&mut self, path: &[u8], _flags: usize) -> Result<usize> {
-        let path_str = str::from_utf8(path).or(Err(Error::NoEntry))?;
-        let id = path_str.parse::<usize>().or(Err(Error::NoEntry))?;
+    fn open(&self, path: &[u8], _flags: usize) -> 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)))?;
+
         if id < COUNTS.lock().len() {
             Ok(id)
         } else {
-            Err(Error::NoEntry)
+            Err(Error::new(ENOENT))
         }
     }
 
-    fn dup(&mut self, file: usize) -> Result<usize> {
+    fn dup(&self, file: usize) -> Result<usize> {
         Ok(file)
     }
 
-    fn read(&mut self, file: usize, buffer: &mut [u8]) -> Result<usize> {
+    fn read(&self, file: usize, buffer: &mut [u8]) -> Result<usize> {
         // Ensures that the length of the buffer is larger than the size of a usize
         if buffer.len() >= mem::size_of::<usize>() {
+            let ack = ACKS.lock()[file];
             let current = COUNTS.lock()[file];
-            loop {
-                let next = COUNTS.lock()[file];
-                if next != current {
-                    // Safe if the length of the buffer is larger than the size of a usize
-                    assert!(buffer.len() >= mem::size_of::<usize>());
-                    unsafe { *(buffer.as_mut_ptr() as *mut usize) = next };
-                    return Ok(mem::size_of::<usize>());
-                } else {
-                    // Safe if all locks have been dropped
-                    unsafe { context::switch(); }
-                }
+            if ack != current {
+                // Safe if the length of the buffer is larger than the size of a usize
+                assert!(buffer.len() >= mem::size_of::<usize>());
+                unsafe { *(buffer.as_mut_ptr() as *mut usize) = current; }
+                Ok(mem::size_of::<usize>())
+            } else {
+                Ok(0)
             }
         } else {
-            Err(Error::InvalidValue)
+            Err(Error::new(EINVAL))
         }
     }
 
-    fn write(&mut self, _file: usize, _buffer: &[u8]) -> Result<usize> {
-        Err(Error::NotPermitted)
+    fn write(&self, file: usize, buffer: &[u8]) -> Result<usize> {
+        if buffer.len() >= mem::size_of::<usize>() {
+            assert!(buffer.len() >= mem::size_of::<usize>());
+            let ack = unsafe { *(buffer.as_ptr() as *const usize) };
+            let current = COUNTS.lock()[file];
+            if ack == current {
+                ACKS.lock()[file] = ack;
+                unsafe { acknowledge(file); }
+                Ok(mem::size_of::<usize>())
+            } else {
+                Ok(0)
+            }
+        } else {
+            Err(Error::new(EINVAL))
+        }
     }
 
-    fn fsync(&mut self, file: usize) -> Result<()> {
-        Ok(())
+    fn fsync(&self, _file: usize) -> Result<usize> {
+        Ok(0)
     }
 
-    fn close(&mut self, file: usize) -> Result<()> {
-        Ok(())
+    fn close(&self, _file: usize) -> Result<usize> {
+        Ok(0)
     }
 }
diff --git a/kernel/scheme/mod.rs b/kernel/scheme/mod.rs
index 6ca5c1357999b2e19baceb954448883f0445255b..4d0580ac07f4aae1566d7c8085d24c9c2e86a32e 100644
--- a/kernel/scheme/mod.rs
+++ b/kernel/scheme/mod.rs
@@ -11,14 +11,16 @@ use alloc::boxed::Box;
 
 use collections::BTreeMap;
 
-use spin::{Once, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
+use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
 
-use syscall::{Error, Result};
+use syscall::error::*;
+use syscall::scheme::Scheme;
 
 use self::debug::DebugScheme;
 use self::env::EnvScheme;
 use self::initfs::InitFsScheme;
 use self::irq::IrqScheme;
+use self::root::RootScheme;
 
 /// Debug scheme
 pub mod debug;
@@ -32,12 +34,18 @@ pub mod initfs;
 /// IRQ handling
 pub mod irq;
 
+/// Root scheme
+pub mod root;
+
+/// Userspace schemes
+pub mod user;
+
 /// Limit on number of schemes
 pub const SCHEME_MAX_SCHEMES: usize = 65536;
 
 /// Scheme list type
 pub struct SchemeList {
-    map: BTreeMap<usize, Arc<Mutex<Box<Scheme + Send>>>>,
+    map: BTreeMap<usize, Arc<Box<Scheme + Send + Sync>>>,
     names: BTreeMap<Box<[u8]>, usize>,
     next_id: usize
 }
@@ -53,11 +61,11 @@ impl SchemeList {
     }
 
     /// Get the nth scheme.
-    pub fn get(&self, id: usize) -> Option<&Arc<Mutex<Box<Scheme + Send>>>> {
+    pub fn get(&self, id: usize) -> Option<&Arc<Box<Scheme + Send + Sync>>> {
         self.map.get(&id)
     }
 
-    pub fn get_name(&self, name: &[u8]) -> Option<(usize, &Arc<Mutex<Box<Scheme + Send>>>)> {
+    pub fn get_name(&self, name: &[u8]) -> Option<(usize, &Arc<Box<Scheme + Send + Sync>>)> {
         if let Some(&id) = self.names.get(name) {
             self.get(id).map(|scheme| (id, scheme))
         } else {
@@ -66,9 +74,9 @@ impl SchemeList {
     }
 
     /// Create a new scheme.
-    pub fn insert(&mut self, name: Box<[u8]>, scheme: Arc<Mutex<Box<Scheme + Send>>>) -> Result<&Arc<Mutex<Box<Scheme + Send>>>> {
+    pub fn insert(&mut self, name: Box<[u8]>, scheme: Arc<Box<Scheme + Send + Sync>>) -> Result<&Arc<Box<Scheme + Send + Sync>>> {
         if self.names.contains_key(&name) {
-            return Err(Error::FileExists);
+            return Err(Error::new(EEXIST));
         }
 
         if self.next_id >= SCHEME_MAX_SCHEMES {
@@ -80,7 +88,7 @@ impl SchemeList {
         }
 
         if self.next_id >= SCHEME_MAX_SCHEMES {
-            return Err(Error::TryAgain);
+            return Err(Error::new(EAGAIN));
         }
 
         let id = self.next_id;
@@ -99,10 +107,11 @@ static SCHEMES: Once<RwLock<SchemeList>> = Once::new();
 /// Initialize schemes, called if needed
 fn init_schemes() -> RwLock<SchemeList> {
     let mut list: SchemeList = SchemeList::new();
-    list.insert(Box::new(*b"debug"), Arc::new(Mutex::new(Box::new(DebugScheme)))).expect("failed to insert debug: scheme");
-    list.insert(Box::new(*b"env"), Arc::new(Mutex::new(Box::new(EnvScheme::new())))).expect("failed to insert env: scheme");
-    list.insert(Box::new(*b"initfs"), Arc::new(Mutex::new(Box::new(InitFsScheme::new())))).expect("failed to insert initfs: scheme");
-    list.insert(Box::new(*b"irq"), Arc::new(Mutex::new(Box::new(IrqScheme)))).expect("failed to insert irq: scheme");
+    list.insert(Box::new(*b""), Arc::new(Box::new(RootScheme::new()))).expect("failed to insert root scheme");
+    list.insert(Box::new(*b"debug"), Arc::new(Box::new(DebugScheme))).expect("failed to insert debug scheme");
+    list.insert(Box::new(*b"env"), Arc::new(Box::new(EnvScheme::new()))).expect("failed to insert env scheme");
+    list.insert(Box::new(*b"initfs"), Arc::new(Box::new(InitFsScheme::new()))).expect("failed to insert initfs scheme");
+    list.insert(Box::new(*b"irq"), Arc::new(Box::new(IrqScheme))).expect("failed to insert irq scheme");
     RwLock::new(list)
 }
 
@@ -115,32 +124,3 @@ pub fn schemes() -> RwLockReadGuard<'static, SchemeList> {
 pub fn schemes_mut() -> RwLockWriteGuard<'static, SchemeList> {
     SCHEMES.call_once(init_schemes).write()
 }
-
-/// A scheme trait, implemented by a scheme handler
-pub trait Scheme {
-    /// Open the file at `path` with `flags`.
-    ///
-    /// Returns a file descriptor or an error
-    fn open(&mut self, path: &[u8], flags: usize) -> Result<usize>;
-
-    /// Duplicate an open file descriptor
-    ///
-    /// Returns a file descriptor or an error
-    fn dup(&mut self, file: usize) -> Result<usize>;
-
-    /// Read from some file descriptor into the `buffer`
-    ///
-    /// Returns the number of bytes read
-    fn read(&mut self, file: usize, buffer: &mut [u8]) -> Result<usize>;
-
-    /// Write the `buffer` to the file descriptor
-    ///
-    /// Returns the number of bytes written
-    fn write(&mut self, file: usize, buffer: &[u8]) -> Result<usize>;
-
-    /// Sync the file descriptor
-    fn fsync(&mut self, file: usize) -> Result<()>;
-
-    /// Close the file descriptor
-    fn close(&mut self, file: usize) -> Result<()>;
-}
diff --git a/kernel/scheme/root.rs b/kernel/scheme/root.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a2e006009fd30fbd567c515b8dce406797cc43b5
--- /dev/null
+++ b/kernel/scheme/root.rs
@@ -0,0 +1,91 @@
+use alloc::arc::Arc;
+use alloc::boxed::Box;
+use collections::BTreeMap;
+use core::sync::atomic::{AtomicUsize, Ordering};
+use spin::RwLock;
+
+use context;
+use syscall::error::*;
+use syscall::scheme::Scheme;
+use scheme;
+use scheme::user::{UserInner, UserScheme};
+
+pub struct RootScheme {
+    next_id: AtomicUsize,
+    handles: RwLock<BTreeMap<usize, Arc<UserInner>>>
+}
+
+impl RootScheme {
+    pub fn new() -> RootScheme {
+        RootScheme {
+            next_id: AtomicUsize::new(0),
+            handles: RwLock::new(BTreeMap::new())
+        }
+    }
+}
+
+impl Scheme for RootScheme {
+    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+        let context = {
+            let contexts = context::contexts();
+            let context = contexts.current().ok_or(Error::new(ESRCH))?;
+            Arc::downgrade(&context)
+        };
+
+        let inner = {
+            let mut schemes = scheme::schemes_mut();
+            if schemes.get_name(path).is_some() {
+                return Err(Error::new(EEXIST));
+            }
+            let inner = Arc::new(UserInner::new(context));
+            schemes.insert(path.to_vec().into_boxed_slice(), Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))).expect("failed to insert user scheme");
+            inner
+        };
+
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(id, inner);
+
+        Ok(id)
+    }
+
+    fn dup(&self, file: usize) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let inner = {
+            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
+            inner.clone()
+        };
+
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        handles.insert(id, inner);
+
+        Ok(id)
+    }
+
+    fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
+        let inner = {
+            let handles = self.handles.read();
+            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
+            inner.clone()
+        };
+
+        inner.read(buf)
+    }
+
+    fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
+        let inner = {
+            let handles = self.handles.read();
+            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
+            inner.clone()
+        };
+
+        inner.write(buf)
+    }
+
+    fn fsync(&self, _file: usize) -> Result<usize> {
+        Ok(0)
+    }
+
+    fn close(&self, file: usize) -> Result<usize> {
+        self.handles.write().remove(&file).ok_or(Error::new(EBADF)).and(Ok(0))
+    }
+}
diff --git a/kernel/scheme/user.rs b/kernel/scheme/user.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f2c797e4e83da3d471636a68dca4dbee9e11bfaa
--- /dev/null
+++ b/kernel/scheme/user.rs
@@ -0,0 +1,258 @@
+use alloc::arc::Weak;
+use collections::{BTreeMap, VecDeque};
+use core::sync::atomic::{AtomicUsize, Ordering};
+use core::{mem, usize};
+use spin::{Mutex, RwLock};
+
+use arch;
+use arch::paging::{InactivePageTable, Page, VirtualAddress, entry};
+use arch::paging::temporary_page::TemporaryPage;
+use context::{self, Context};
+use context::memory::Grant;
+use syscall::data::{Packet, Stat};
+use syscall::error::*;
+use syscall::number::*;
+use syscall::scheme::Scheme;
+
+pub struct UserInner {
+    next_id: AtomicUsize,
+    context: Weak<RwLock<Context>>,
+    todo: Mutex<VecDeque<Packet>>,
+    done: Mutex<BTreeMap<usize, usize>>
+}
+
+impl UserInner {
+    pub fn new(context: Weak<RwLock<Context>>) -> UserInner {
+        UserInner {
+            next_id: AtomicUsize::new(0),
+            context: context,
+            todo: Mutex::new(VecDeque::new()),
+            done: Mutex::new(BTreeMap::new())
+        }
+    }
+
+    pub fn call(&self, a: usize, b: usize, c: usize, d: usize) -> Result<usize> {
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+
+        let packet = Packet {
+            id: id,
+            a: a,
+            b: b,
+            c: c,
+            d: d
+        };
+
+        self.todo.lock().push_back(packet);
+
+        loop {
+            {
+                let mut done = self.done.lock();
+                if let Some(a) = done.remove(&id) {
+                    return Error::demux(a);
+                }
+            }
+
+            unsafe { context::switch(); }
+        }
+    }
+
+    pub fn capture(&self, buf: &[u8]) -> Result<usize> {
+        self.capture_inner(buf.as_ptr() as usize, buf.len(), false)
+    }
+
+    pub fn capture_mut(&self, buf: &mut [u8]) -> Result<usize> {
+        self.capture_inner(buf.as_mut_ptr() as usize, buf.len(), true)
+    }
+
+    fn capture_inner(&self, address: usize, size: usize, writable: bool) -> Result<usize> {
+        if size == 0 {
+            Ok(0)
+        } else {
+            let context_lock = self.context.upgrade().ok_or(Error::new(ESRCH))?;
+            let context = context_lock.read();
+
+            let mut grants = context.grants.lock();
+
+            let mut new_table = unsafe { InactivePageTable::from_address(context.arch.get_page_table()) };
+            let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(arch::USER_TMP_GRANT_OFFSET)));
+
+            let from_address = (address/4096) * 4096;
+            let offset = address - from_address;
+            let full_size = ((offset + size + 4095)/4096) * 4096;
+            let mut to_address = arch::USER_GRANT_OFFSET;
+
+            let mut flags = entry::PRESENT | entry::NO_EXECUTE;
+            if writable {
+                flags |= entry::WRITABLE;
+            }
+
+            for i in 0 .. grants.len() {
+                let start = grants[i].start_address().get();
+                if to_address + full_size < start {
+                    grants.insert(i, Grant::new(
+                        VirtualAddress::new(from_address),
+                        VirtualAddress::new(to_address),
+                        full_size,
+                        flags,
+                        &mut new_table,
+                        &mut temporary_page
+                    ));
+
+                    return Ok(to_address + offset);
+                } else {
+                    let pages = (grants[i].size() + 4095) / 4096;
+                    let end = start + pages * 4096;
+                    to_address = end;
+                }
+            }
+
+            grants.push(Grant::new(
+                VirtualAddress::new(from_address),
+                VirtualAddress::new(to_address),
+                full_size,
+                flags,
+                &mut new_table,
+                &mut temporary_page
+            ));
+
+            Ok(to_address + offset)
+        }
+    }
+
+    pub fn release(&self, address: usize) -> Result<()> {
+        if address == 0 {
+            Ok(())
+        } else {
+            let context_lock = self.context.upgrade().ok_or(Error::new(ESRCH))?;
+            let context = context_lock.read();
+
+            let mut grants = context.grants.lock();
+
+            let mut new_table = unsafe { InactivePageTable::from_address(context.arch.get_page_table()) };
+            let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(arch::USER_TMP_GRANT_OFFSET)));
+
+            for i in 0 .. grants.len() {
+                let start = grants[i].start_address().get();
+                let end = start + grants[i].size();
+                if address >= start && address < end {
+                    grants.remove(i).destroy(&mut new_table, &mut temporary_page);
+
+                    return Ok(());
+                }
+            }
+
+            Err(Error::new(EFAULT))
+        }
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
+        let packet_size = mem::size_of::<Packet>();
+        let len = buf.len()/packet_size;
+        if len > 0 {
+            loop {
+                let mut i = 0;
+                {
+                    let mut todo = self.todo.lock();
+                    while ! todo.is_empty() && i < len {
+                        let packet = todo.pop_front().unwrap();
+                        unsafe { *(buf.as_mut_ptr() as *mut Packet).offset(i as isize) = packet; }
+                        i += 1;
+                    }
+                }
+
+                if i > 0 {
+                    return Ok(i * packet_size);
+                } else {
+                    unsafe { context::switch(); }
+                }
+            }
+        } else {
+            Ok(0)
+        }
+    }
+
+    pub fn write(&self, buf: &[u8]) -> Result<usize> {
+        let packet_size = mem::size_of::<Packet>();
+        let len = buf.len()/packet_size;
+        let mut i = 0;
+        while i < len {
+            let packet = unsafe { *(buf.as_ptr() as *const Packet).offset(i as isize) };
+            self.done.lock().insert(packet.id, packet.a);
+            i += 1;
+        }
+
+        Ok(i * packet_size)
+    }
+}
+
+/// UserInner has to be wrapped
+pub struct UserScheme {
+    inner: Weak<UserInner>
+}
+
+impl UserScheme {
+    pub fn new(inner: Weak<UserInner>) -> UserScheme {
+        UserScheme {
+            inner: inner
+        }
+    }
+}
+
+impl Scheme for UserScheme {
+    fn open(&self, path: &[u8], flags: usize) -> 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);
+        let _ = inner.release(address);
+        result
+    }
+
+    fn dup(&self, file: usize) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        inner.call(SYS_DUP, file, 0, 0)
+    }
+
+    fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        let address = inner.capture_mut(buf)?;
+        let result = inner.call(SYS_READ, file, address, buf.len());
+        let _ = inner.release(address);
+        result
+    }
+
+    fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        let address = inner.capture(buf)?;
+        let result = inner.call(SYS_WRITE, file, address, buf.len());
+        let _ = inner.release(address);
+        result
+    }
+
+    fn seek(&self, file: usize, position: usize, whence: usize) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        inner.call(SYS_FSYNC, file, position, whence)
+    }
+
+    fn fstat(&self, file: usize, stat: &mut Stat) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        let address = inner.capture_mut(stat)?;
+        let result = inner.call(SYS_FSTAT, file, address, 0);
+        let _ = inner.release(address);
+        result
+    }
+
+    fn fsync(&self, file: usize) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        inner.call(SYS_FSYNC, file, 0, 0)
+    }
+
+    fn ftruncate(&self, file: usize, len: usize) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        inner.call(SYS_FTRUNCATE, file, len, 0)
+    }
+
+    fn close(&self, file: usize) -> Result<usize> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+        inner.call(SYS_CLOSE, file, 0, 0)
+    }
+}
diff --git a/kernel/syscall/call.rs b/kernel/syscall/call.rs
deleted file mode 100644
index 3bcefd4830cb0f681b1231b7bacc4bab3fb8a632..0000000000000000000000000000000000000000
--- a/kernel/syscall/call.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use super::{Error, Result};
-
-/// System call list
-/// See http://syscalls.kernelgrok.com/ for numbers
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-#[repr(C)]
-pub enum Call {
-    /// Exit syscall
-    Exit = 1,
-    /// Read syscall
-    Read = 3,
-    /// Write syscall
-    Write = 4,
-    /// Open syscall
-    Open = 5,
-    /// Close syscall
-    Close = 6,
-    /// Wait for a process
-    WaitPid = 7,
-    /// Execute syscall
-    Exec = 11,
-    /// Change working directory
-    ChDir = 12,
-    /// Get process ID
-    GetPid = 20,
-    /// Duplicate file descriptor
-    Dup = 41,
-    /// Set process break
-    Brk = 45,
-    /// Set process I/O privilege level
-    Iopl = 110,
-    /// Sync file descriptor
-    FSync = 118,
-    /// Clone process
-    Clone = 120,
-    /// Yield to scheduler
-    SchedYield = 158,
-    /// Get process working directory
-    GetCwd = 183
-}
-
-/// Convert numbers to calls
-/// See http://syscalls.kernelgrok.com/
-impl Call {
-    //TODO: Return Option<Call>
-    pub fn from(number: usize) -> Result<Call> {
-        match number {
-            1 => Ok(Call::Exit),
-            3 => Ok(Call::Read),
-            4 => Ok(Call::Write),
-            5 => Ok(Call::Open),
-            6 => Ok(Call::Close),
-            7 => Ok(Call::WaitPid),
-            11 => Ok(Call::Exec),
-            12 => Ok(Call::ChDir),
-            20 => Ok(Call::GetPid),
-            41 => Ok(Call::Dup),
-            45 => Ok(Call::Brk),
-            110 => Ok(Call::Iopl),
-            118 => Ok(Call::FSync),
-            120 => Ok(Call::Clone),
-            158 => Ok(Call::SchedYield),
-            183 => Ok(Call::GetCwd),
-            _ => Err(Error::NoCall)
-        }
-    }
-}
diff --git a/kernel/syscall/error.rs b/kernel/syscall/error.rs
deleted file mode 100644
index ad37b336e0fdf80cd1d237ddbf2bac8a5cd9ce88..0000000000000000000000000000000000000000
--- a/kernel/syscall/error.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-/// The error number for an invalid value
-/// See http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html for numbers
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-#[repr(C)]
-pub enum Error {
-    /// Operation not permitted
-    NotPermitted = 1,
-    /// No such file or directory
-    NoEntry = 2,
-    /// No such process
-    NoProcess = 3,
-    /// Invalid executable format
-    NoExec = 8,
-    /// Bad file number
-    BadFile = 9,
-    /// Try again
-    TryAgain = 11,
-    /// File exists
-    FileExists = 17,
-    /// Invalid argument
-    InvalidValue = 22,
-    /// Too many open files
-    TooManyFiles = 24,
-    /// Syscall not implemented
-    NoCall = 38
-}
-
-pub type Result<T> = ::core::result::Result<T, Error>;
diff --git a/kernel/syscall/fs.rs b/kernel/syscall/fs.rs
index d68727d500740d3b9218a8e222b4ec18780415fa..029222b78297fbc2b8010a9b5792eac8e316fdae 100644
--- a/kernel/syscall/fs.rs
+++ b/kernel/syscall/fs.rs
@@ -2,21 +2,23 @@
 
 use context;
 use scheme;
+use syscall::data::Stat;
+use syscall::error::*;
 
-use super::{Error, Result};
-
+/// Change the current working directory
 pub fn chdir(path: &[u8]) -> Result<usize> {
     let contexts = context::contexts();
-    let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
     let context = context_lock.read();
     let canonical = context.canonicalize(path);
     *context.cwd.lock() = canonical;
     Ok(0)
 }
 
+/// Get the current working directory
 pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
     let contexts = context::contexts();
-    let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
     let context = context_lock.read();
     let cwd = context.cwd.lock();
     let mut i = 0;
@@ -27,43 +29,11 @@ pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
     Ok(i)
 }
 
-/// Read syscall
-pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> {
-    let file = {
-        let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::NoProcess)?;
-        let context = context_lock.read();
-        let file = context.get_file(fd).ok_or(Error::BadFile)?;
-        file
-    };
-
-    let schemes = scheme::schemes();
-    let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?;
-    let result = scheme_mutex.lock().read(file.number, buf);
-    result
-}
-
-/// Write syscall
-pub fn write(fd: usize, buf: &[u8]) -> Result<usize> {
-    let file = {
-        let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::NoProcess)?;
-        let context = context_lock.read();
-        let file = context.get_file(fd).ok_or(Error::BadFile)?;
-        file
-    };
-
-    let schemes = scheme::schemes();
-    let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?;
-    let result = scheme_mutex.lock().write(file.number, buf);
-    result
-}
-
 /// Open syscall
 pub fn open(path: &[u8], flags: usize) -> Result<usize> {
     let path_canon = {
         let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
         context.canonicalize(path)
     };
@@ -73,65 +43,147 @@ pub fn open(path: &[u8], flags: usize) -> Result<usize> {
     let reference_opt = parts.next();
 
     let (scheme_id, file_id) = {
-        let namespace = namespace_opt.ok_or(Error::NoEntry)?;
-        let schemes = scheme::schemes();
-        let (scheme_id, scheme_mutex) = schemes.get_name(namespace).ok_or(Error::NoEntry)?;
-        let file_id = scheme_mutex.lock().open(reference_opt.unwrap_or(b""), flags)?;
+        let namespace = namespace_opt.ok_or(Error::new(ENOENT))?;
+        let (scheme_id, scheme) = {
+            let schemes = scheme::schemes();
+            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)?;
         (scheme_id, file_id)
     };
 
     let contexts = context::contexts();
-    let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
     let context = context_lock.read();
     context.add_file(::context::file::File {
         scheme: scheme_id,
         number: file_id
-    }).ok_or(Error::TooManyFiles)
+    }).ok_or(Error::new(EMFILE))
 }
 
 /// Close syscall
 pub fn close(fd: usize) -> Result<usize> {
     let file = {
         let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        let file = context.remove_file(fd).ok_or(Error::BadFile)?;
+        let file = context.remove_file(fd).ok_or(Error::new(EBADF))?;
         file
     };
 
-    let schemes = scheme::schemes();
-    let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?;
-    let result = scheme_mutex.lock().close(file.number).and(Ok(0));
-    result
+    let scheme = {
+        let schemes = scheme::schemes();
+        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+        scheme.clone()
+    };
+    scheme.close(file.number)
 }
 
 /// Duplicate file descriptor
 pub fn dup(fd: usize) -> Result<usize> {
     let file = {
         let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
+        file
+    };
+
+    let scheme = {
+        let schemes = scheme::schemes();
+        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+        scheme.clone()
+    };
+    scheme.dup(file.number)
+}
+
+/// Get information about the file
+pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> {
+    let file = {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        let file = context.get_file(fd).ok_or(Error::BadFile)?;
+        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
         file
     };
 
-    let schemes = scheme::schemes();
-    let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?;
-    let result = scheme_mutex.lock().dup(file.number);
-    result
+    let scheme = {
+        let schemes = scheme::schemes();
+        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+        scheme.clone()
+    };
+    scheme.fstat(file.number, stat)
 }
 
+/// Sync the file descriptor
 pub fn fsync(fd: usize) -> Result<usize> {
     let file = {
         let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        let file = context.get_file(fd).ok_or(Error::BadFile)?;
+        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
         file
     };
 
-    let schemes = scheme::schemes();
-    let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?;
-    let result = scheme_mutex.lock().fsync(file.number).and(Ok(0));
-    result
+    let scheme = {
+        let schemes = scheme::schemes();
+        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+        scheme.clone()
+    };
+    scheme.fsync(file.number)
+}
+
+/// Seek to an offset
+pub fn lseek(fd: usize, pos: usize, whence: usize) -> Result<usize> {
+    let file = {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
+        file
+    };
+
+    let scheme = {
+        let schemes = scheme::schemes();
+        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+        scheme.clone()
+    };
+    scheme.seek(file.number, pos, whence)
+}
+
+/// Read syscall
+pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> {
+    let file = {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
+        file
+    };
+
+    let scheme = {
+        let schemes = scheme::schemes();
+        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+        scheme.clone()
+    };
+    scheme.read(file.number, buf)
+}
+
+/// Write syscall
+pub fn write(fd: usize, buf: &[u8]) -> Result<usize> {
+    let file = {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
+        file
+    };
+
+    let scheme = {
+        let schemes = scheme::schemes();
+        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+        scheme.clone()
+    };
+    scheme.write(file.number, buf)
 }
diff --git a/kernel/syscall/mod.rs b/kernel/syscall/mod.rs
index 396f366ac94daf343c2d0faeeb702a040b314763..b37a056be9bc4e00b96492a32f55b4c16003a7b2 100644
--- a/kernel/syscall/mod.rs
+++ b/kernel/syscall/mod.rs
@@ -1,58 +1,55 @@
 ///! Syscall handlers
 
-pub use self::call::*;
-pub use self::error::*;
+extern crate syscall;
+
+pub use self::syscall::{data, error, flag, number, scheme};
+
 pub use self::fs::*;
 pub use self::process::*;
 pub use self::validate::*;
 
-/// System call numbers
-mod call;
-
-/// System error codes
-mod error;
+use self::data::Stat;
+use self::error::{Error, Result, ENOSYS};
+use self::number::*;
 
 /// Filesystem syscalls
-mod fs;
+pub mod fs;
 
 /// Process syscalls
-mod process;
+pub mod process;
 
 /// Validate input
-mod validate;
+pub mod validate;
 
 #[no_mangle]
 pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> usize {
     #[inline(always)]
     fn inner(a: usize, b: usize, c: usize, d: usize, e: usize, _f: usize, stack: usize) -> Result<usize> {
-        match Call::from(a) {
-            Ok(call) => match call {
-                Call::Exit => exit(b),
-                Call::Read => read(b, validate_slice_mut(c as *mut u8, d)?),
-                Call::Write => write(b, validate_slice(c as *const u8, d)?),
-                Call::Open => open(validate_slice(b as *const u8, c)?, d),
-                Call::Close => close(b),
-                Call::WaitPid => waitpid(b, c, d),
-                Call::Exec => exec(validate_slice(b as *const u8, c)?, validate_slice(d as *const [usize; 2], e)?),
-                Call::ChDir => chdir(validate_slice(b as *const u8, c)?),
-                Call::GetPid => getpid(),
-                Call::Dup => dup(b),
-                Call::Brk => brk(b),
-                Call::Iopl => iopl(b),
-                Call::FSync => fsync(b),
-                Call::Clone => clone(b, stack),
-                Call::SchedYield => sched_yield(),
-                Call::GetCwd => getcwd(validate_slice_mut(b as *mut u8, c)?)
-            },
-            Err(err) => {
+        match a {
+            SYS_EXIT => exit(b),
+            SYS_READ => read(b, validate_slice_mut(c as *mut u8, d)?),
+            SYS_WRITE => write(b, validate_slice(c as *const u8, d)?),
+            SYS_OPEN => open(validate_slice(b as *const u8, c)?, d),
+            SYS_CLOSE => close(b),
+            SYS_WAITPID => waitpid(b, c, d),
+            SYS_EXECVE => exec(validate_slice(b as *const u8, c)?, validate_slice(d as *const [usize; 2], e)?),
+            SYS_CHDIR => chdir(validate_slice(b as *const u8, c)?),
+            SYS_LSEEK => lseek(b, c, d),
+            SYS_GETPID => getpid(),
+            SYS_FSTAT => fstat(b, &mut validate_slice_mut(b as *mut Stat, 1)?[0]),
+            SYS_DUP => dup(b),
+            SYS_BRK => brk(b),
+            SYS_IOPL => iopl(b),
+            SYS_FSYNC => fsync(b),
+            SYS_CLONE => clone(b, stack),
+            SYS_YIELD => sched_yield(),
+            SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?),
+            _ => {
                 println!("Unknown syscall {}", a);
-                Err(err)
+                Err(Error::new(ENOSYS))
             }
         }
     }
 
-    match inner(a, b, c, d, e, f, stack) {
-        Ok(value) => value,
-        Err(value) => (-(value as isize)) as usize
-    }
+    Error::mux(inner(a, b, c, d, e, f, stack))
 }
diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs
index 383141cb36df7488c457fb2fd748e0b14ae955ee..3d94a1170563073b5a29c08c81b0e4b051cb84ff 100644
--- a/kernel/syscall/process.rs
+++ b/kernel/syscall/process.rs
@@ -15,11 +15,13 @@ use arch::start::usermode;
 use context;
 use elf::{self, program_header};
 use scheme;
-use syscall::{self, Error, Result, validate_slice, validate_slice_mut};
+use syscall;
+use syscall::error::*;
+use syscall::validate::{validate_slice, validate_slice_mut};
 
 pub fn brk(address: usize) -> Result<usize> {
     let contexts = context::contexts();
-    let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
     let context = context_lock.read();
 
     let current = if let Some(ref heap_shared) = context.heap {
@@ -45,8 +47,7 @@ pub fn brk(address: usize) -> Result<usize> {
 
         Ok(address)
     } else {
-        //TODO: Return correct error
-        Err(Error::NotPermitted)
+        Err(Error::new(ENOMEM))
     }
 }
 
@@ -75,7 +76,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
         // Copy from old process
         {
             let contexts = context::contexts();
-            let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
             let context = context_lock.read();
 
             ppid = context.id;
@@ -96,11 +97,11 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
 
             if flags & CLONE_VM == CLONE_VM {
                 for memory_shared in context.image.iter() {
-                    image.push(memory_shared.borrow());
+                    image.push(memory_shared.clone());
                 }
 
                 if let Some(ref heap_shared) = context.heap {
-                    heap_option = Some(heap_shared.borrow());
+                    heap_option = Some(heap_shared.clone());
                 }
             } else {
                 for memory_shared in context.image.iter() {
@@ -174,28 +175,38 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
             if flags & CLONE_FILES == CLONE_FILES {
                 files = context.files.clone();
             } else {
-                let mut files_vec = Vec::new();
-                for (fd, file_option) in context.files.lock().iter().enumerate() {
-                    if let Some(file) = *file_option {
-                        let result = {
+                files = Arc::new(Mutex::new(context.files.lock().clone()));
+            }
+        }
+
+        // If not cloning files, dup to get a new number from scheme
+        // This has to be done outside the context lock to prevent deadlocks
+        if flags & CLONE_FILES == 0 {
+            for (fd, mut file_option) in files.lock().iter_mut().enumerate() {
+                let new_file_option = if let Some(file) = *file_option {
+                    let result = {
+                        let scheme = {
                             let schemes = scheme::schemes();
-                            let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?;
-                            let result = scheme_mutex.lock().dup(file.number);
-                            result
+                            let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+                            scheme.clone()
                         };
-                        match result {
-                            Ok(new_number) => {
-                                files_vec.push(Some(context::file::File { scheme: file.scheme, number: new_number }));
-                            },
-                            Err(err) => {
-                                println!("clone: failed to dup {}: {:?}", fd, err);
-                            }
+                        let result = scheme.dup(file.number);
+                        result
+                    };
+                    match result {
+                        Ok(new_number) => {
+                            Some(context::file::File { scheme: file.scheme, number: new_number })
+                        },
+                        Err(err) => {
+                            println!("clone: failed to dup {}: {:?}", fd, err);
+                            None
                         }
-                    } else {
-                        files_vec.push(None);
                     }
-                }
-                files = Arc::new(Mutex::new(files_vec));
+                } else {
+                    None
+                };
+
+                *file_option = new_file_option;
             }
         }
 
@@ -349,7 +360,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
         //TODO: Only read elf header, not entire file. Then read required segments
         let mut data = vec![];
         loop {
-            let mut buf = [0; 4096];
+            let mut buf = [0; 16384];
             let count = syscall::read(file, &mut buf)?;
             if count > 0 {
                 data.extend_from_slice(&buf[..count]);
@@ -367,7 +378,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
                 drop(arg_ptrs); // Drop so that usage is not allowed after unmapping context
 
                 let contexts = context::contexts();
-                let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+                let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                 let mut context = context_lock.write();
 
                 // Unmap previous image and stack
@@ -468,7 +479,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
             },
             Err(err) => {
                 println!("failed to execute {}: {}", unsafe { str::from_utf8_unchecked(path) }, err);
-                return Err(Error::NoExec);
+                return Err(Error::new(ENOEXEC));
             }
         }
     }
@@ -479,7 +490,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
 
 pub fn getpid() -> Result<usize> {
     let contexts = context::contexts();
-    let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
     let context = context_lock.read();
     Ok(context.id)
 }
@@ -502,7 +513,7 @@ pub fn waitpid(pid: usize, status_ptr: usize, _options: usize) -> Result<usize>
 
             {
                 let contexts = context::contexts();
-                let context_lock = contexts.get(pid).ok_or(Error::NoProcess)?;
+                let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
                 let context = context_lock.read();
                 if let context::Status::Exited(status) = context.status {
                     if status_ptr != 0 {
@@ -515,7 +526,7 @@ pub fn waitpid(pid: usize, status_ptr: usize, _options: usize) -> Result<usize>
 
             if exited {
                 let mut contexts = context::contexts_mut();
-                return contexts.remove(pid).ok_or(Error::NoProcess).and(Ok(pid));
+                return contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid));
             }
         }
 
diff --git a/kernel/syscall/validate.rs b/kernel/syscall/validate.rs
index 61c73841f34b454f9346bbb39b938d0af94cd6cb..361f58d94ff67af2a26da80ece4ec52a828b766b 100644
--- a/kernel/syscall/validate.rs
+++ b/kernel/syscall/validate.rs
@@ -1,15 +1,39 @@
-use core::slice;
+use core::{mem, slice};
 
-use super::Result;
+use arch::paging::{ActivePageTable, Page, VirtualAddress, entry};
+use syscall::error::*;
+
+fn validate(address: usize, size: usize, flags: entry::EntryFlags) -> Result<()> {
+    let active_table = unsafe { ActivePageTable::new() };
+
+    let start_page = Page::containing_address(VirtualAddress::new(address));
+    let end_page = Page::containing_address(VirtualAddress::new(address + size - 1));
+    for page in Page::range_inclusive(start_page, end_page) {
+        let page_flags = active_table.translate_page_flags(page).ok_or(Error::new(EFAULT))?;
+        if ! page_flags.contains(flags) {
+            return Err(Error::new(EFAULT));
+        }
+    }
+
+    Ok(())
+}
 
 /// Convert a pointer and length to slice, if valid
-/// TODO: Check validity
 pub fn validate_slice<T>(ptr: *const T, len: usize) -> Result<&'static [T]> {
-    Ok(unsafe { slice::from_raw_parts(ptr, len) })
+    if len == 0 {
+        Ok(&[])
+    } else {
+        validate(ptr as usize, len * mem::size_of::<T>(), entry::PRESENT /* TODO | entry::USER_ACCESSIBLE */)?;
+        Ok(unsafe { slice::from_raw_parts(ptr, len) })
+    }
 }
 
 /// Convert a pointer and length to slice, if valid
-/// TODO: Check validity
 pub fn validate_slice_mut<T>(ptr: *mut T, len: usize) -> Result<&'static mut [T]> {
-    Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
+    if len == 0 {
+        Ok(&mut [])
+    } else {
+        validate(ptr as usize, len * mem::size_of::<T>(), entry::PRESENT | entry::WRITABLE /* TODO | entry::USER_ACCESSIBLE */)?;
+        Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
+    }
 }
diff --git a/kernel/tests/mod.rs b/kernel/tests/mod.rs
index 3b92c184e3a178dbe1637a250b39fdd8e2474a60..0ad27af5ce61b8fcc27d8c1cf7638ba42e85bb69 100644
--- a/kernel/tests/mod.rs
+++ b/kernel/tests/mod.rs
@@ -24,6 +24,6 @@ fn stdio() {
 /// Test that invalid reads/writes cause errors
 #[test]
 fn invalid_path() {
-    assert_eq!(syscall::read(999, &mut []), Err(Error::BadFile));
-    assert_eq!(syscall::write(999, &[]), Err(Error::BadFile));
+    assert_eq!(syscall::read(999, &mut []), Err(Error::new(EBADF)));
+    assert_eq!(syscall::write(999, &[]), Err(Error::new(EBADF)));
 }
diff --git a/libstd b/libstd
index 0d434cc168b1d88211f1a3b72c8290c911432826..ae80aff4d39b2d3a83f7d32bc95739e4e1169184 160000
--- a/libstd
+++ b/libstd
@@ -1 +1 @@
-Subproject commit 0d434cc168b1d88211f1a3b72c8290c911432826
+Subproject commit ae80aff4d39b2d3a83f7d32bc95739e4e1169184
diff --git a/schemes/example/Cargo.toml b/schemes/example/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..68b930ff85f351f49246030fc041d6605135ee2b
--- /dev/null
+++ b/schemes/example/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "example"
+version = "0.1.0"
+
+[dependencies.syscall]
+path = "../../syscall/"
diff --git a/schemes/example/src/main.rs b/schemes/example/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..84fb34587bcb7d1d3947c2a2961d221382d0d0ed
--- /dev/null
+++ b/schemes/example/src/main.rs
@@ -0,0 +1,39 @@
+extern crate syscall;
+
+use std::fs::File;
+use std::io::{Read, Write};
+use std::str;
+use std::thread;
+
+use syscall::{Packet, Result, Scheme};
+
+struct ExampleScheme;
+
+impl Scheme for ExampleScheme {
+    fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
+        println!("{}", unsafe { str::from_utf8_unchecked(path) });
+        Ok(0)
+    }
+
+    fn dup(&self, file: usize) -> Result<usize> {
+        Ok(file)
+    }
+
+    fn close(&self, _file: usize) -> Result<usize> {
+        Ok(0)
+    }
+}
+
+fn main(){
+    thread::spawn(move || {
+        let mut socket = File::create(":example").expect("example: failed to create example scheme");
+        let scheme = ExampleScheme;
+        loop {
+            let mut packet = Packet::default();
+            socket.read(&mut packet).expect("example: failed to read events from example scheme");
+            println!("{:?}", packet);
+            scheme.handle(&mut packet);
+            socket.write(&packet).expect("example: failed to write responses to example scheme");
+        }
+    });
+}
diff --git a/syscall/src/data.rs b/syscall/src/data.rs
new file mode 100644
index 0000000000000000000000000000000000000000..399950bcc9efcd4fec90cefe2b4dae1b629bbe0d
--- /dev/null
+++ b/syscall/src/data.rs
@@ -0,0 +1,69 @@
+use core::ops::{Deref, DerefMut};
+use core::{mem, slice};
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(packed)]
+pub struct Packet {
+    pub id: usize,
+    pub a: usize,
+    pub b: usize,
+    pub c: usize,
+    pub d: usize
+}
+
+impl Deref for Packet {
+    type Target = [u8];
+    fn deref(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self as *const Packet as *const u8, mem::size_of::<Packet>()) as &[u8]
+        }
+    }
+}
+
+impl DerefMut for Packet {
+    fn deref_mut(&mut self) -> &mut [u8] {
+        unsafe {
+            slice::from_raw_parts_mut(self as *mut Packet as *mut u8, mem::size_of::<Packet>()) as &mut [u8]
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(packed)]
+pub struct Stat {
+    pub st_dev: u16,
+    pub st_ino: u16,
+    pub st_mode: u16,
+    pub st_nlink: u16,
+    pub st_uid: u16,
+    pub st_gid: u16,
+    pub st_rdev: u16,
+    pub st_size: u32,
+    pub st_atime: u32,
+    pub st_mtime: u32,
+    pub st_ctime: u32
+}
+
+impl Deref for Stat {
+    type Target = [u8];
+    fn deref(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self as *const Stat as *const u8, mem::size_of::<Stat>()) as &[u8]
+        }
+    }
+}
+
+impl DerefMut for Stat {
+    fn deref_mut(&mut self) -> &mut [u8] {
+        unsafe {
+            slice::from_raw_parts_mut(self as *mut Stat as *mut u8, mem::size_of::<Stat>()) as &mut [u8]
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(packed)]
+pub struct TimeSpec {
+    pub tv_sec: i64,
+    pub tv_nsec: i32,
+}
diff --git a/syscall/src/error.rs b/syscall/src/error.rs
index 9af8f4ab99b7c93f18a19e5f6c2aeecdb0371dc0..5481f9fc8eff78d519285ba1b1f4fcdf3d24271e 100644
--- a/syscall/src/error.rs
+++ b/syscall/src/error.rs
@@ -1,5 +1,6 @@
 use core::{fmt, result};
 
+#[derive(Eq, PartialEq)]
 pub struct Error {
     pub errno: isize,
 }
diff --git a/syscall/src/flag.rs b/syscall/src/flag.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fa00558e82b125c90392e312c70277d774dc517d
--- /dev/null
+++ b/syscall/src/flag.rs
@@ -0,0 +1,40 @@
+pub const CLONE_VM: usize = 0x100;
+pub const CLONE_FS: usize = 0x200;
+pub const CLONE_FILES: usize = 0x400;
+pub const CLONE_VFORK: usize = 0x4000;
+/// Mark this clone as supervised.
+///
+/// This means that the process can run in supervised mode, even not being connected to
+/// a supervisor yet. In other words, the parent can later on supervise the process and handle
+/// the potential blocking syscall.
+///
+/// This is an important security measure, since otherwise the process would be able to fork it
+/// self right after starting, making supervising it impossible.
+pub const CLONE_SUPERVISE: usize = 0x400000;
+pub const CLOCK_REALTIME: usize = 1;
+pub const CLOCK_MONOTONIC: usize = 4;
+
+pub const MODE_DIR: u16 = 0x4000;
+pub const MODE_FILE: u16 = 0x8000;
+pub const MODE_ALL: u16 = MODE_DIR | MODE_FILE;
+
+pub const FUTEX_WAIT: usize = 0;
+pub const FUTEX_WAKE: usize = 1;
+pub const FUTEX_REQUEUE: usize = 2;
+
+pub const SEEK_SET: usize = 0;
+pub const SEEK_CUR: usize = 1;
+pub const SEEK_END: usize = 2;
+
+pub const O_RDONLY: usize = 0;
+pub const O_WRONLY: usize = 1;
+pub const O_RDWR: usize = 2;
+pub const O_NONBLOCK: usize = 4;
+pub const O_APPEND: usize = 8;
+pub const O_SHLOCK: usize = 0x10;
+pub const O_EXLOCK: usize = 0x20;
+pub const O_ASYNC: usize = 0x40;
+pub const O_FSYNC: usize = 0x80;
+pub const O_CREAT: usize = 0x200;
+pub const O_TRUNC: usize = 0x400;
+pub const O_EXCL: usize = 0x800;
diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs
index 800a1db141a733d84b41793fc377003342bc4da0..099f61fdb08d330fb206104fe365c48a0a38e1fc 100644
--- a/syscall/src/lib.rs
+++ b/syscall/src/lib.rs
@@ -2,105 +2,29 @@
 #![no_std]
 
 pub use self::arch::*;
+pub use self::data::*;
 pub use self::error::*;
+pub use self::flag::*;
+pub use self::number::*;
+pub use self::scheme::*;
 
 #[cfg(target_arch = "x86")]
 #[path="x86.rs"]
-pub mod arch;
+mod arch;
 
 #[cfg(target_arch = "x86_64")]
 #[path="x86_64.rs"]
-pub mod arch;
+mod arch;
+
+pub mod data;
 
 pub mod error;
 
-pub const SYS_BRK: usize = 45;
-pub const SYS_CHDIR: usize = 12;
-pub const SYS_CLONE: usize = 120;
-    pub const CLONE_VM: usize = 0x100;
-    pub const CLONE_FS: usize = 0x200;
-    pub const CLONE_FILES: usize = 0x400;
-    pub const CLONE_VFORK: usize = 0x4000;
-    /// Mark this clone as supervised.
-    ///
-    /// This means that the process can run in supervised mode, even not being connected to
-    /// a supervisor yet. In other words, the parent can later on supervise the process and handle
-    /// the potential blocking syscall.
-    ///
-    /// This is an important security measure, since otherwise the process would be able to fork it
-    /// self right after starting, making supervising it impossible.
-    pub const CLONE_SUPERVISE: usize = 0x400000;
-pub const SYS_CLOSE: usize = 6;
-pub const SYS_CLOCK_GETTIME: usize = 265;
-    pub const CLOCK_REALTIME: usize = 1;
-    pub const CLOCK_MONOTONIC: usize = 4;
-pub const SYS_DUP: usize = 41;
-pub const SYS_EXECVE: usize = 11;
-pub const SYS_EXIT: usize = 1;
-pub const SYS_FPATH: usize = 928;
-pub const SYS_FSTAT: usize = 28;
-    pub const MODE_DIR: u16 = 0x4000;
-    pub const MODE_FILE: u16 = 0x8000;
-    pub const MODE_ALL: u16 = MODE_DIR | MODE_FILE;
-pub const SYS_FSYNC: usize = 118;
-pub const SYS_FTRUNCATE: usize = 93;
-pub const SYS_FUTEX: usize = 240;
-    pub const FUTEX_WAIT: usize = 0;
-    pub const FUTEX_WAKE: usize = 1;
-    pub const FUTEX_REQUEUE: usize = 2;
-pub const SYS_GETCWD: usize = 183;
-pub const SYS_GETPID: usize = 20;
-pub const SYS_IOPL: usize = 110;
-pub const SYS_LINK: usize = 9;
-pub const SYS_LSEEK: usize = 19;
-    pub const SEEK_SET: usize = 0;
-    pub const SEEK_CUR: usize = 1;
-    pub const SEEK_END: usize = 2;
-pub const SYS_MKDIR: usize = 39;
-pub const SYS_NANOSLEEP: usize = 162;
-pub const SYS_OPEN: usize = 5;
-    pub const O_RDONLY: usize = 0;
-    pub const O_WRONLY: usize = 1;
-    pub const O_RDWR: usize = 2;
-    pub const O_NONBLOCK: usize = 4;
-    pub const O_APPEND: usize = 8;
-    pub const O_SHLOCK: usize = 0x10;
-    pub const O_EXLOCK: usize = 0x20;
-    pub const O_ASYNC: usize = 0x40;
-    pub const O_FSYNC: usize = 0x80;
-    pub const O_CREAT: usize = 0x200;
-    pub const O_TRUNC: usize = 0x400;
-    pub const O_EXCL: usize = 0x800;
-pub const SYS_PIPE2: usize = 331;
-pub const SYS_READ: usize = 3;
-pub const SYS_RMDIR: usize = 84;
-pub const SYS_UNLINK: usize = 10;
-pub const SYS_WAITPID: usize = 7;
-pub const SYS_WRITE: usize = 4;
-pub const SYS_YIELD: usize = 158;
-
-#[derive(Copy, Clone, Debug, Default)]
-#[repr(packed)]
-pub struct Stat {
-    pub st_dev: u16,
-    pub st_ino: u16,
-    pub st_mode: u16,
-    pub st_nlink: u16,
-    pub st_uid: u16,
-    pub st_gid: u16,
-    pub st_rdev: u16,
-    pub st_size: u32,
-    pub st_atime: u32,
-    pub st_mtime: u32,
-    pub st_ctime: u32
-}
-
-#[derive(Copy, Clone, Debug, Default)]
-#[repr(packed)]
-pub struct TimeSpec {
-    pub tv_sec: i64,
-    pub tv_nsec: i32,
-}
+pub mod flag;
+
+pub mod number;
+
+pub mod scheme;
 
 pub unsafe fn brk(addr: usize) -> Result<usize> {
     syscall1(SYS_BRK, addr)
diff --git a/syscall/src/number.rs b/syscall/src/number.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e65c37ff2183e24f4a4ebbad876def83239e5f14
--- /dev/null
+++ b/syscall/src/number.rs
@@ -0,0 +1,28 @@
+pub const SYS_BRK: usize = 45;
+pub const SYS_CHDIR: usize = 12;
+pub const SYS_CLONE: usize = 120;
+pub const SYS_CLOSE: usize = 6;
+pub const SYS_CLOCK_GETTIME: usize = 265;
+pub const SYS_DUP: usize = 41;
+pub const SYS_EXECVE: usize = 11;
+pub const SYS_EXIT: usize = 1;
+pub const SYS_FPATH: usize = 928;
+pub const SYS_FSTAT: usize = 28;
+pub const SYS_FSYNC: usize = 118;
+pub const SYS_FTRUNCATE: usize = 93;
+pub const SYS_FUTEX: usize = 240;
+pub const SYS_GETCWD: usize = 183;
+pub const SYS_GETPID: usize = 20;
+pub const SYS_IOPL: usize = 110;
+pub const SYS_LINK: usize = 9;
+pub const SYS_LSEEK: usize = 19;
+pub const SYS_MKDIR: usize = 39;
+pub const SYS_NANOSLEEP: usize = 162;
+pub const SYS_OPEN: usize = 5;
+pub const SYS_PIPE2: usize = 331;
+pub const SYS_READ: usize = 3;
+pub const SYS_RMDIR: usize = 84;
+pub const SYS_UNLINK: usize = 10;
+pub const SYS_WAITPID: usize = 7;
+pub const SYS_WRITE: usize = 4;
+pub const SYS_YIELD: usize = 158;
diff --git a/syscall/src/scheme.rs b/syscall/src/scheme.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6a857d1518aca505a92f62e4e24150f6c9d20c71
--- /dev/null
+++ b/syscall/src/scheme.rs
@@ -0,0 +1,94 @@
+use core::slice;
+
+use super::*;
+
+pub trait Scheme {
+    fn handle(&self, packet: &mut Packet) {
+        packet.a = Error::mux(match packet.a {
+            SYS_OPEN => self.open(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.d),
+            SYS_MKDIR => self.mkdir(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.d),
+            SYS_RMDIR => self.rmdir(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }),
+            SYS_UNLINK => self.unlink(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }),
+
+            SYS_DUP => self.dup(packet.b),
+            SYS_READ => self.read(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }),
+            SYS_WRITE => self.write(packet.b, unsafe { slice::from_raw_parts(packet.c as *const u8, packet.d) }),
+            SYS_LSEEK => self.seek(packet.b, packet.c, packet.d),
+            SYS_FPATH => self.fpath(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }),
+            SYS_FSTAT => self.fstat(packet.b, unsafe { &mut *(packet.c as *mut Stat) }),
+            SYS_FSYNC => self.fsync(packet.b),
+            SYS_FTRUNCATE => self.ftruncate(packet.b, packet.c),
+            SYS_CLOSE => self.close(packet.b),
+
+            _ => Err(Error::new(ENOSYS))
+        });
+    }
+
+    /* Scheme operations */
+
+    #[allow(unused_variables)]
+    fn open(&self, path: &[u8], flags: usize) -> Result<usize> {
+        Err(Error::new(ENOENT))
+    }
+
+    #[allow(unused_variables)]
+    fn mkdir(&self, path: &[u8], mode: usize) -> Result<usize> {
+        Err(Error::new(ENOENT))
+    }
+
+    #[allow(unused_variables)]
+    fn rmdir(&self, path: &[u8]) -> Result<usize> {
+        Err(Error::new(ENOENT))
+    }
+
+    #[allow(unused_variables)]
+    fn unlink(&self, path: &[u8]) -> Result<usize> {
+        Err(Error::new(ENOENT))
+    }
+
+    /* Resource operations */
+    #[allow(unused_variables)]
+    fn dup(&self, old_id: usize) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn fsync(&self, id: usize) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn ftruncate(&self, id: usize, len: usize) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+
+    #[allow(unused_variables)]
+    fn close(&self, id: usize) -> Result<usize> {
+        Err(Error::new(EBADF))
+    }
+}