diff --git a/src/context/context.rs b/src/context/context.rs
index 56e3e10f9331b67fdbb4ca558b7cec33317791ff..1cf5a900f8aa502d34a06e70f397995b1d7557ba 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -244,17 +244,28 @@ impl Context {
     /// Add a file to the lowest available slot.
     /// Return the file descriptor number or None if no slot was found
     pub fn add_file(&self, file: FileDescriptor) -> Option<FileHandle> {
+        self.add_file_min(file, 0)
+    }
+
+    /// Add a file to the lowest available slot greater than or equal to min.
+    /// Return the file descriptor number or None if no slot was found
+    pub fn add_file_min(&self, file: FileDescriptor, min: usize) -> Option<FileHandle> {
         let mut files = self.files.lock();
         for (i, mut file_option) in files.iter_mut().enumerate() {
-            if file_option.is_none() {
+            if file_option.is_none() && i >= min {
                 *file_option = Some(file);
                 return Some(FileHandle::from(i));
             }
         }
         let len = files.len();
         if len < super::CONTEXT_MAX_FILES {
-            files.push(Some(file));
-            Some(FileHandle::from(len))
+            if len >= min {
+                files.push(Some(file));
+                Some(FileHandle::from(len))
+            } else {
+                drop(files);
+                self.insert_file(FileHandle::from(min), file)
+            }
         } else {
             None
         }
diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs
index 76fcfd6f9c09dde85de91a51205ec60ead9ddcff..168d165cebd9a9303b402489b141e6e9ffa0e50b 100644
--- a/src/syscall/fs.rs
+++ b/src/syscall/fs.rs
@@ -8,7 +8,7 @@ use scheme::{self, FileHandle};
 use syscall;
 use syscall::data::{Packet, Stat};
 use syscall::error::*;
-use syscall::flag::{F_GETFD, F_SETFD, F_GETFL, F_SETFL, O_ACCMODE, O_RDONLY, O_WRONLY, MODE_DIR, MODE_FILE, O_CLOEXEC};
+use syscall::flag::{F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_DUPFD, O_ACCMODE, O_RDONLY, O_WRONLY, MODE_DIR, MODE_FILE, O_CLOEXEC};
 use context::file::{FileDescriptor, FileDescription};
 
 pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> {
@@ -240,8 +240,7 @@ pub fn close(fd: FileHandle) -> Result<usize> {
     file.close(fd)
 }
 
-/// Duplicate file descriptor
-pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
+fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result<FileDescriptor> {
     let file = {
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
@@ -251,15 +250,11 @@ pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
     };
 
     if buf.is_empty() {
-        let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
-        let context = context_lock.read();
-
-        context.add_file(FileDescriptor {
+        Ok(FileDescriptor {
             description: Arc::clone(&file.description),
             event: None,
             cloexec: false,
-        }).ok_or(Error::new(EMFILE))
+        })
     } else {
         let description = file.description.read();
 
@@ -273,11 +268,7 @@ pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
             scheme.dup(description.number, buf)?
         };
 
-        let contexts = context::contexts();
-        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
-        let context = context_lock.read();
-
-        context.add_file(FileDescriptor {
+        Ok(FileDescriptor {
             description: Arc::new(RwLock::new(FileDescription {
                 scheme: description.scheme,
                 number: new_id,
@@ -285,61 +276,34 @@ pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
             })),
             event: None,
             cloexec: false,
-        }).ok_or(Error::new(EMFILE))
+        })
     }
 }
 
+/// Duplicate file descriptor
+pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
+    let new_file = duplicate_file(fd, buf)?;
+
+    let contexts = context::contexts();
+    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+    let context = context_lock.read();
+
+    context.add_file(new_file).ok_or(Error::new(EMFILE))
+}
+
 /// Duplicate file descriptor, replacing another
 pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
     if fd == new_fd {
         Ok(new_fd)
     } else {
         let _ = close(new_fd);
+        let new_file = duplicate_file(fd, buf)?;
 
-        let file = {
-            let contexts = context::contexts();
-            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
-            let context = context_lock.read();
-            context.get_file(fd).ok_or(Error::new(EBADF))?
-        };
-
-        if buf.is_empty() {
-            let contexts = context::contexts();
-            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
-            let context = context_lock.read();
-
-            context.insert_file(new_fd, FileDescriptor {
-                description: Arc::clone(&file.description),
-                event: None,
-                cloexec: false,
-            }).ok_or(Error::new(EBADF))
-        } else {
-            let description = file.description.read();
-
-            let new_id = {
-                let scheme = {
-                    let schemes = scheme::schemes();
-                    let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?;
-
-                    scheme.clone()
-                };
-                scheme.dup(description.number, buf)?
-            };
-
-            let contexts = context::contexts();
-            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
-            let context = context_lock.read();
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
 
-            context.insert_file(new_fd, FileDescriptor {
-                description: Arc::new(RwLock::new(FileDescription {
-                    scheme: description.scheme,
-                    number: new_id,
-                    flags: description.flags,
-                })),
-                event: None,
-                cloexec: false,
-            }).ok_or(Error::new(EMFILE))
-        }
+        context.insert_file(new_fd, new_file).ok_or(Error::new(EMFILE))
     }
 }
 
@@ -356,7 +320,7 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result<usize> {
     let description = file.description.read();
 
     // Communicate fcntl with scheme
-    if cmd != F_GETFD && cmd != F_SETFD {
+    if cmd != F_DUPFD && cmd != F_GETFD && cmd != F_SETFD {
         let scheme = {
             let schemes = scheme::schemes();
             let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?;
@@ -367,9 +331,23 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result<usize> {
 
     // Perform kernel operation if scheme agrees
     {
+        if cmd == F_DUPFD {
+            // Not in match because 'files' cannot be locked
+            let new_file = duplicate_file(fd, &[])?;
+
+            let contexts = context::contexts();
+            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+            let context = context_lock.read();
+
+            return context.add_file_min(new_file, arg)
+                .ok_or(Error::new(EMFILE))
+                .map(FileHandle::into);
+        }
+
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
+
         let mut files = context.files.lock();
         match *files.get_mut(fd.into()).ok_or(Error::new(EBADF))? {
             Some(ref mut file) => match cmd {