diff --git a/context/context.rs b/context/context.rs
index 9dcc10beacfb18372cb0a7f66fa6e77e20b320af..504b5e4666378715a4e2aeed67b795f4c4ead548 100644
--- a/context/context.rs
+++ b/context/context.rs
@@ -1,6 +1,6 @@
 use alloc::arc::Arc;
 use alloc::boxed::Box;
-use collections::Vec;
+use collections::{BTreeMap, Vec};
 use spin::Mutex;
 
 use arch;
@@ -39,6 +39,8 @@ pub struct Context {
     pub grants: Arc<Mutex<Vec<Grant>>>,
     /// The current working directory
     pub cwd: Arc<Mutex<Vec<u8>>>,
+    /// The process environment
+    pub env: Arc<Mutex<BTreeMap<Box<[u8]>, Arc<Mutex<Vec<u8>>>>>>,
     /// The open files in the scheme
     pub files: Arc<Mutex<Vec<Option<File>>>>
 }
@@ -58,6 +60,7 @@ impl Context {
             stack: None,
             grants: Arc::new(Mutex::new(Vec::new())),
             cwd: Arc::new(Mutex::new(Vec::new())),
+            env: Arc::new(Mutex::new(BTreeMap::new())),
             files: Arc::new(Mutex::new(Vec::new()))
         }
     }
diff --git a/scheme/env.rs b/scheme/env.rs
index d1f494feb3b6052cf0e76fa07588384beefa7bce..7f2bcb83fcc05494d474b899663c816605910905 100644
--- a/scheme/env.rs
+++ b/scheme/env.rs
@@ -1,39 +1,31 @@
-use collections::BTreeMap;
+use alloc::arc::Arc;
+use collections::{BTreeMap, Vec};
 use core::{cmp, str};
 use core::sync::atomic::{AtomicUsize, Ordering};
-use spin::RwLock;
+use spin::{Mutex, RwLock};
 
+use context;
 use syscall::data::Stat;
 use syscall::error::*;
-use syscall::flag::{MODE_DIR, MODE_FILE, SEEK_SET, SEEK_CUR, SEEK_END};
+use syscall::flag::{MODE_FILE, SEEK_SET, SEEK_CUR, SEEK_END};
 use syscall::scheme::Scheme;
 
+#[derive(Clone)]
 struct Handle {
-    data: &'static [u8],
+    data: Arc<Mutex<Vec<u8>>>,
     mode: u16,
     seek: usize
 }
 
 pub struct EnvScheme {
     next_id: AtomicUsize,
-    files: BTreeMap<&'static [u8], &'static [u8]>,
     handles: RwLock<BTreeMap<usize, Handle>>
 }
 
 impl EnvScheme {
     pub fn new() -> EnvScheme {
-        let mut files: BTreeMap<&'static [u8], &'static [u8]> = BTreeMap::new();
-
-        files.insert(b"", b"COLUMNS\nHOME\nLINES\nPATH\nPWD");
-        files.insert(b"COLUMNS", b"80");
-        files.insert(b"LINES", b"30");
-        files.insert(b"HOME", b"initfs:bin/");
-        files.insert(b"PATH", b"initfs:bin/");
-        files.insert(b"PWD", b"initfs:bin/");
-
         EnvScheme {
             next_id: AtomicUsize::new(0),
-            files: files,
             handles: RwLock::new(BTreeMap::new())
         }
     }
@@ -42,31 +34,69 @@ impl EnvScheme {
 impl Scheme for EnvScheme {
     fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
         let path = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?.trim_matches('/');
-        let data = self.files.get(path.as_bytes()).ok_or(Error::new(ENOENT))?;
 
-        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
-        self.handles.write().insert(id, Handle {
-            data: data,
-            mode: if path.is_empty() { MODE_DIR } else { MODE_FILE },
-            seek: 0
-        });
+        let env_lock = {
+            let contexts = context::contexts();
+            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+            let context = context_lock.read();
+            context.env.clone()
+        };
 
-        Ok(id)
+        if path.is_empty() {
+            let mut list = Vec::new();
+            {
+                let env = env_lock.lock();
+                for entry in env.iter() {
+                    if ! list.is_empty() {
+                        list.push(b'\n');
+                    }
+                    list.extend_from_slice(&entry.0);
+                    list.push(b'=');
+                    list.extend_from_slice(&entry.1.lock());
+                }
+            }
+
+            let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+            self.handles.write().insert(id, Handle {
+                data: Arc::new(Mutex::new(list)),
+                mode: MODE_FILE,
+                seek: 0
+            });
+
+            Ok(id)
+        } else {
+            let data = {
+                let mut env = env_lock.lock();
+                if env.contains_key(path.as_bytes()) {
+                    env[path.as_bytes()].clone()
+                } else /*if flags & O_CREAT == O_CREAT*/ {
+                    let name = path.as_bytes().to_vec().into_boxed_slice();
+                    let data = Arc::new(Mutex::new(Vec::new()));
+                    env.insert(name, data.clone());
+                    data
+                }
+            };
+
+            let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+            self.handles.write().insert(id, Handle {
+                data: data,
+                mode: MODE_FILE,
+                seek: 0
+            });
+
+            Ok(id)
+        }
     }
 
     fn dup(&self, id: usize) -> Result<usize> {
-        let (data, mode, seek) = {
+        let new_handle = {
             let handles = self.handles.read();
             let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
-            (handle.data, handle.mode, handle.seek)
+            handle.clone()
         };
 
         let id = self.next_id.fetch_add(1, Ordering::SeqCst);
-        self.handles.write().insert(id, Handle {
-            data: data,
-            mode: mode,
-            seek: seek
-        });
+        self.handles.write().insert(id, new_handle);
 
         Ok(id)
     }
@@ -75,9 +105,33 @@ impl Scheme for EnvScheme {
         let mut handles = self.handles.write();
         let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
 
+        let data = handle.data.lock();
+
         let mut i = 0;
-        while i < buffer.len() && handle.seek < handle.data.len() {
-            buffer[i] = handle.data[handle.seek];
+        while i < buffer.len() && handle.seek < data.len() {
+            buffer[i] = data[handle.seek];
+            i += 1;
+            handle.seek += 1;
+        }
+
+        Ok(i)
+    }
+
+    fn write(&self, id: usize, buffer: &[u8]) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
+
+        let mut data = handle.data.lock();
+
+        let mut i = 0;
+        while i < buffer.len() && handle.seek < data.len() {
+            data[handle.seek] = buffer[i];
+            i += 1;
+            handle.seek += 1;
+        }
+
+        while i < buffer.len() {
+            data.push(buffer[i]);
             i += 1;
             handle.seek += 1;
         }
@@ -89,10 +143,11 @@ impl Scheme for EnvScheme {
         let mut handles = self.handles.write();
         let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
 
+        let len = handle.data.lock().len();
         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,
+            SEEK_SET => cmp::min(len, pos),
+            SEEK_CUR => cmp::max(0, cmp::min(len as isize, handle.seek as isize + pos as isize)) as usize,
+            SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
             _ => return Err(Error::new(EINVAL))
         };
 
@@ -104,12 +159,31 @@ impl Scheme for EnvScheme {
         let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
 
         stat.st_mode = handle.mode;
-        stat.st_size = handle.data.len() as u32; //TODO: st_size 64-bit
+        stat.st_size = handle.data.lock().len() as u32; //TODO: st_size 64-bit
+
+        Ok(0)
+    }
+
+    fn fsync(&self, id: usize) -> Result<usize> {
+        let handles = self.handles.read();
+        let _handle = handles.get(&id).ok_or(Error::new(EBADF))?;
 
         Ok(0)
     }
 
-    fn fsync(&self, _id: usize) -> Result<usize> {
+    fn ftruncate(&self, id: usize, len: usize) -> Result<usize> {
+        let handles = self.handles.read();
+        let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
+
+        let mut data = handle.data.lock();
+        if len < data.len() {
+            data.truncate(len)
+        } else {
+            while len > data.len() {
+                data.push(0);
+            }
+        }
+
         Ok(0)
     }
 
diff --git a/syscall/fs.rs b/syscall/fs.rs
index 646a05ca6c6a0ba86da03f6f608698b513c67534..2fef6f64afee6caaef2b97323f2aa20f04f58f4a 100644
--- a/syscall/fs.rs
+++ b/syscall/fs.rs
@@ -152,6 +152,24 @@ pub fn fsync(fd: usize) -> Result<usize> {
     scheme.fsync(file.number)
 }
 
+/// Truncate the file descriptor
+pub fn ftruncate(fd: usize, len: 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.ftruncate(file.number, len)
+}
+
 /// Seek to an offset
 pub fn lseek(fd: usize, pos: usize, whence: usize) -> Result<usize> {
     let file = {
diff --git a/syscall/mod.rs b/syscall/mod.rs
index 2c507ea4f219218cdbff8d2a9763333200cc1eb3..c4ed06b772ba2bc9448c03af870201dab70f795d 100644
--- a/syscall/mod.rs
+++ b/syscall/mod.rs
@@ -39,6 +39,7 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
             SYS_FSTAT => fstat(b, &mut validate_slice_mut(c as *mut Stat, 1)?[0]),
             SYS_DUP => dup(b),
             SYS_BRK => brk(b),
+            SYS_FTRUNCATE => ftruncate(b, c),
             SYS_IOPL => iopl(b),
             SYS_FSYNC => fsync(b),
             SYS_CLONE => clone(b, stack),
diff --git a/syscall/process.rs b/syscall/process.rs
index bc61d38720b1ce9dedd9509a2475a23991fd21f3..0da2d00e9816047ce85d745f33b1fc475e92b96a 100644
--- a/syscall/process.rs
+++ b/syscall/process.rs
@@ -66,6 +66,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
         let mut stack_option = None;
         let grants;
         let cwd;
+        let env;
         let files;
 
         // Copy from old process
@@ -181,6 +182,12 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
                 cwd = Arc::new(Mutex::new(context.cwd.lock().clone()));
             }
 
+            if flags & CLONE_VM == CLONE_VM {
+                env = context.env.clone();
+            } else {
+                env = Arc::new(Mutex::new(context.env.lock().clone()));
+            }
+
             if flags & CLONE_FILES == CLONE_FILES {
                 files = context.files.clone();
             } else {
@@ -339,6 +346,8 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
 
             context.cwd = cwd;
 
+            context.env = env;
+
             context.files = files;
 
             context.arch.set_page_table(unsafe { new_table.address() });