From d036c667a1ad7e104cd48846bf6b90961da8c1fb Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Sun, 16 Apr 2017 12:49:54 -0600
Subject: [PATCH] Perform cloexec logic in kernel

---
 src/context/file.rs      |  2 ++
 src/scheme/initfs.rs     |  6 +----
 src/scheme/pipe.rs       | 48 ++++++++++++++--------------------------
 src/scheme/root.rs       |  7 +-----
 src/scheme/sys/iostat.rs |  6 ++---
 src/syscall/fs.rs        | 39 +++++++++++++++++++++++++++++++-
 src/syscall/mod.rs       |  1 +
 src/syscall/process.rs   | 24 ++++++++++++--------
 8 files changed, 77 insertions(+), 56 deletions(-)

diff --git a/src/context/file.rs b/src/context/file.rs
index 26e0863c..e7519b63 100644
--- a/src/context/file.rs
+++ b/src/context/file.rs
@@ -10,6 +10,8 @@ pub struct File {
     pub scheme: SchemeId,
     /// The number the scheme uses to refer to this file
     pub number: usize,
+    /// The flags passed to open or fcntl(SETFL)
+    pub flags: usize,
     /// If events are on, this is the event ID
     pub event: Option<usize>,
 }
diff --git a/src/scheme/initfs.rs b/src/scheme/initfs.rs
index 432e62af..827b0f99 100644
--- a/src/scheme/initfs.rs
+++ b/src/scheme/initfs.rs
@@ -65,17 +65,13 @@ impl Scheme for InitFsScheme {
         Err(Error::new(ENOENT))
     }
 
-    fn dup(&self, id: usize, buf: &[u8]) -> Result<usize> {
+    fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
         let (path, flags, data, mode, seek) = {
             let handles = self.handles.read();
             let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
             (handle.path, handle.flags, handle.data, handle.mode, handle.seek)
         };
 
-        if buf == b"exec" && flags & O_CLOEXEC == O_CLOEXEC {
-            return Err(Error::new(EBADF));
-        }
-
         let id = self.next_id.fetch_add(1, Ordering::SeqCst);
         self.handles.write().insert(id, Handle {
             path: path,
diff --git a/src/scheme/pipe.rs b/src/scheme/pipe.rs
index e03312c8..7de8636b 100644
--- a/src/scheme/pipe.rs
+++ b/src/scheme/pipe.rs
@@ -6,7 +6,7 @@ use scheme::{AtomicSchemeId, ATOMIC_SCHEMEID_INIT, SchemeId};
 
 use sync::WaitCondition;
 use syscall::error::{Error, Result, EAGAIN, EBADF, EINVAL, EPIPE};
-use syscall::flag::{F_GETFL, F_SETFL, O_ACCMODE, O_CLOEXEC, O_NONBLOCK};
+use syscall::flag::{F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
 use syscall::scheme::Scheme;
 
 /// Pipes list
@@ -50,11 +50,11 @@ impl PipeScheme {
 }
 
 impl Scheme for PipeScheme {
-    fn dup(&self, id: usize, buf: &[u8]) -> Result<usize> {
+    fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
         let mut pipes = pipes_mut();
 
         let read_option = if let Some(pipe) = pipes.0.get(&id) {
-            Some(pipe.dup(buf)?)
+            Some(pipe.dup()?)
         } else {
             None
         };
@@ -65,7 +65,7 @@ impl Scheme for PipeScheme {
         }
 
         let write_option = if let Some(pipe) = pipes.1.get(&id) {
-            Some(pipe.dup(buf)?)
+            Some(pipe.dup()?)
         } else {
             None
         };
@@ -152,20 +152,12 @@ impl PipeRead {
         }
     }
 
-    fn dup(&self, buf: &[u8]) -> Result<Self> {
-        if buf == b"exec" && self.flags.load(Ordering::SeqCst) & O_CLOEXEC == O_CLOEXEC {
-            Err(Error::new(EBADF))
-        } else {
-            let mut flags = self.flags.load(Ordering::SeqCst);
-            if buf.is_empty() {
-                flags &= ! O_CLOEXEC;
-            }
-            Ok(PipeRead {
-                flags: AtomicUsize::new(flags),
-                condition: self.condition.clone(),
-                vec: self.vec.clone()
-            })
-        }
+    fn dup(&self) -> Result<Self> {
+        Ok(PipeRead {
+            flags: AtomicUsize::new(self.flags.load(Ordering::SeqCst)),
+            condition: self.condition.clone(),
+            vec: self.vec.clone()
+        })
     }
 
     fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
@@ -226,20 +218,12 @@ impl PipeWrite {
         }
     }
 
-    fn dup(&self, buf: &[u8]) -> Result<Self> {
-        if buf == b"exec" && self.flags.load(Ordering::SeqCst) & O_CLOEXEC == O_CLOEXEC {
-            Err(Error::new(EBADF))
-        } else {
-            let mut flags = self.flags.load(Ordering::SeqCst);
-            if buf.is_empty() {
-                flags &= ! O_CLOEXEC;
-            }
-            Ok(PipeWrite {
-                flags: AtomicUsize::new(flags),
-                condition: self.condition.clone(),
-                vec: self.vec.clone()
-            })
-        }
+    fn dup(&self) -> Result<Self> {
+        Ok(PipeWrite {
+            flags: AtomicUsize::new(self.flags.load(Ordering::SeqCst)),
+            condition: self.condition.clone(),
+            vec: self.vec.clone()
+        })
     }
 
     fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
diff --git a/src/scheme/root.rs b/src/scheme/root.rs
index 644144ae..cd08fd36 100644
--- a/src/scheme/root.rs
+++ b/src/scheme/root.rs
@@ -6,7 +6,6 @@ use spin::RwLock;
 
 use context;
 use syscall::error::*;
-use syscall::flag::O_CLOEXEC;
 use syscall::scheme::Scheme;
 use scheme::{self, SchemeNamespace, SchemeId};
 use scheme::user::{UserInner, UserScheme};
@@ -59,17 +58,13 @@ impl Scheme for RootScheme {
         }
     }
 
-    fn dup(&self, file: usize, buf: &[u8]) -> Result<usize> {
+    fn dup(&self, file: usize, _buf: &[u8]) -> Result<usize> {
         let mut handles = self.handles.write();
         let inner = {
             let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
             inner.clone()
         };
 
-        if buf == b"exec" && inner.flags & O_CLOEXEC == O_CLOEXEC {
-            return Err(Error::new(EBADF));
-        }
-
         let id = self.next_id.fetch_add(1, Ordering::SeqCst);
         handles.insert(id, inner);
 
diff --git a/src/scheme/sys/iostat.rs b/src/scheme/sys/iostat.rs
index f7316bb1..9bb34e6c 100644
--- a/src/scheme/sys/iostat.rs
+++ b/src/scheme/sys/iostat.rs
@@ -36,7 +36,7 @@ pub fn resource() -> Result<Vec<u8>> {
                     match schemes.get(file.scheme) {
                         Some(scheme) => scheme.clone(),
                         None => {
-                            let _ = writeln!(string, "  {:>4}: {:>8} {:>8}: no scheme", fd, file.scheme.into(), file.number);
+                            let _ = writeln!(string, "  {:>4}: {:>8} {:>8} {:>08X}: no scheme", fd, file.scheme.into(), file.number, file.flags);
                             continue;
                         }
                     }
@@ -46,10 +46,10 @@ pub fn resource() -> Result<Vec<u8>> {
                 match scheme.fpath(file.number, &mut fpath) {
                     Ok(path_len) => {
                         let fname = str::from_utf8(&fpath[..path_len]).unwrap_or("?");
-                        let _ = writeln!(string, "  {:>4}: {:>8} {:>8}: {}", fd, file.scheme.into(), file.number, fname);
+                        let _ = writeln!(string, "{:>6}: {:>8} {:>8} {:>08X}: {}", fd, file.scheme.into(), file.number, file.flags, fname);
                     },
                     Err(err) => {
-                        let _ = writeln!(string, "  {:>4}: {:>8} {:>8}: {}", fd, file.scheme.into(), file.number, err);
+                        let _ = writeln!(string, "{:>6}: {:>8} {:>8} {:>08X}: {}", fd, file.scheme.into(), file.number, file.flags, err);
                     }
                 }
             }
diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs
index bd02c52a..2b80c41d 100644
--- a/src/syscall/fs.rs
+++ b/src/syscall/fs.rs
@@ -6,7 +6,7 @@ use scheme::{self, FileHandle};
 use syscall;
 use syscall::data::{Packet, Stat};
 use syscall::error::*;
-use syscall::flag::{MODE_DIR, MODE_FILE};
+use syscall::flag::{F_SETFL, O_ACCMODE, O_RDONLY, O_WRONLY, MODE_DIR, MODE_FILE};
 
 pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> {
     let (file, pid, uid, gid) = {
@@ -112,6 +112,7 @@ pub fn open(path: &[u8], flags: usize) -> Result<FileHandle> {
     context.add_file(::context::file::File {
         scheme: scheme_id,
         number: file_id,
+        flags: flags,
         event: None,
     }).ok_or(Error::new(EMFILE))
 }
@@ -128,12 +129,14 @@ pub fn pipe2(fds: &mut [usize], flags: usize) -> Result<usize> {
         let read_fd = context.add_file(::context::file::File {
             scheme: scheme_id,
             number: read_id,
+            flags: O_RDONLY | flags & !O_ACCMODE,
             event: None,
         }).ok_or(Error::new(EMFILE))?;
 
         let write_fd = context.add_file(::context::file::File {
             scheme: scheme_id,
             number: write_id,
+            flags: O_WRONLY | flags & !O_ACCMODE,
             event: None,
         }).ok_or(Error::new(EMFILE))?;
 
@@ -259,6 +262,7 @@ pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
     context.add_file(::context::file::File {
         scheme: file.scheme,
         number: new_id,
+        flags: file.flags,
         event: None,
     }).ok_or(Error::new(EMFILE))
 }
@@ -293,11 +297,44 @@ pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: &[u8]) -> Result<FileHandle
         context.insert_file(new_fd, ::context::file::File {
             scheme: file.scheme,
             number: new_id,
+            flags: file.flags,
             event: None,
         }).ok_or(Error::new(EBADF))
     }
 }
 
+// File descriptor controls
+pub fn fcntl(fd: FileHandle, cmd: usize, arg: 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 res = {
+        let scheme = {
+            let schemes = scheme::schemes();
+            let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
+            scheme.clone()
+        };
+        scheme.fcntl(file.number, cmd, arg)?
+    };
+
+    if cmd == F_SETFL {
+        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();
+        let mut file = files.get_mut(fd.into()).ok_or(Error::new(EBADF))?.ok_or(Error::new(EBADF))?;
+        let accmode = file.flags & O_ACCMODE;
+        file.flags = accmode | arg & !O_ACCMODE;
+    }
+
+    Ok(res)
+}
+
 /// Register events for file
 pub fn fevent(fd: FileHandle, flags: usize) -> Result<usize> {
     let file = {
diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs
index be0eef9c..1d9cea69 100644
--- a/src/syscall/mod.rs
+++ b/src/syscall/mod.rs
@@ -54,6 +54,7 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
                         SYS_CLOSE => close(fd),
                         SYS_DUP => dup(fd, validate_slice(c as *const u8, d)?).map(FileHandle::into),
                         SYS_DUP2 => dup2(fd, FileHandle::from(c), validate_slice(d as *const u8, e)?).map(FileHandle::into),
+                        SYS_FCNTL => fcntl(fd, c, d),
                         SYS_FEVENT => fevent(fd, c),
                         SYS_FUNMAP => funmap(b),
                         _ => file_op(a, fd, c, d)
diff --git a/src/syscall/process.rs b/src/syscall/process.rs
index 96a0d953..a3028612 100644
--- a/src/syscall/process.rs
+++ b/src/syscall/process.rs
@@ -18,7 +18,7 @@ use scheme::{self, FileHandle};
 use syscall;
 use syscall::data::Stat;
 use syscall::error::*;
-use syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, WNOHANG};
+use syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, O_CLOEXEC, WNOHANG};
 use syscall::validate::{validate_slice, validate_slice_mut};
 
 pub fn brk(address: usize) -> Result<usize> {
@@ -268,6 +268,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
                             Some(context::file::File {
                                 scheme: file.scheme,
                                 number: new_number,
+                                flags: file.flags,
                                 event: None,
                             })
                         },
@@ -733,15 +734,19 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
                     let new_file_option = if let Some(file) = *file_option {
                         // Duplicate
                         let result = {
-                            let scheme_option = {
-                                let schemes = scheme::schemes();
-                                schemes.get(file.scheme).map(|scheme| scheme.clone())
-                            };
-                            if let Some(scheme) = scheme_option {
-                                let result = scheme.dup(file.number, b"exec");
-                                result
-                            } else {
+                            if file.flags & O_CLOEXEC == O_CLOEXEC {
                                 Err(Error::new(EBADF))
+                            } else {
+                                let scheme_option = {
+                                    let schemes = scheme::schemes();
+                                    schemes.get(file.scheme).map(|scheme| scheme.clone())
+                                };
+                                if let Some(scheme) = scheme_option {
+                                    let result = scheme.dup(file.number, b"exec");
+                                    result
+                                } else {
+                                    Err(Error::new(EBADF))
+                                }
                             }
                         };
 
@@ -766,6 +771,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
                                 Some(context::file::File {
                                     scheme: file.scheme,
                                     number: new_number,
+                                    flags: file.flags,
                                     event: None,
                                 })
                             },
-- 
GitLab