From c4fb60f216a6b9103b6e74e2d775ec74f6c971be Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Tue, 11 Apr 2017 21:27:39 -0600
Subject: [PATCH] Implement script file support

---
 src/syscall/process.rs | 77 ++++++++++++++++++++++++++++++------------
 1 file changed, 55 insertions(+), 22 deletions(-)

diff --git a/src/syscall/process.rs b/src/syscall/process.rs
index abee4bc1..8a8fc1c7 100644
--- a/src/syscall/process.rs
+++ b/src/syscall/process.rs
@@ -488,6 +488,14 @@ fn empty(context: &mut context::Context, reaping: bool) {
     }
 }
 
+struct ExecFile(FileHandle);
+
+impl Drop for ExecFile {
+    fn drop(&mut self) {
+        let _ = syscall::close(self.0);
+    }
+}
+
 pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
     let entry;
     let mut sp = ::USER_STACK_OFFSET + ::USER_STACK_SIZE - 256;
@@ -499,37 +507,62 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
             args.push(arg.to_vec()); // Must be moved into kernel space before exec unmaps all memory
         }
 
-        let (uid, gid, canonical) = {
+        let (uid, gid, mut canonical) = {
             let contexts = context::contexts();
             let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
             let context = context_lock.read();
             (context.euid, context.egid, context.canonicalize(path))
         };
 
-        let file = syscall::open(&canonical, syscall::flag::O_RDONLY)?;
-        let mut stat = Stat::default();
-        syscall::file_op_mut_slice(syscall::number::SYS_FSTAT, file, &mut stat)?;
+        let mut stat: Stat;
+        let mut data: Vec<u8>;
+        loop {
+            let file = ExecFile(syscall::open(&canonical, syscall::flag::O_RDONLY)?);
 
-        let mut perm = stat.st_mode & 0o7;
-        if stat.st_uid == uid {
-            perm |= (stat.st_mode >> 6) & 0o7;
-        }
-        if stat.st_gid == gid {
-            perm |= (stat.st_mode >> 3) & 0o7;
-        }
-        if uid == 0 {
-            perm |= 0o7;
-        }
+            stat = Stat::default();
+            syscall::file_op_mut_slice(syscall::number::SYS_FSTAT, file.0, &mut stat)?;
 
-        if perm & 0o1 != 0o1 {
-            let _ = syscall::close(file);
-            return Err(Error::new(EACCES));
-        }
+            let mut perm = stat.st_mode & 0o7;
+            if stat.st_uid == uid {
+                perm |= (stat.st_mode >> 6) & 0o7;
+            }
+            if stat.st_gid == gid {
+                perm |= (stat.st_mode >> 3) & 0o7;
+            }
+            if uid == 0 {
+                perm |= 0o7;
+            }
 
-        //TODO: Only read elf header, not entire file. Then read required segments
-        let mut data = vec![0; stat.st_size as usize];
-        syscall::file_op_mut_slice(syscall::number::SYS_READ, file, &mut data)?;
-        let _ = syscall::close(file);
+            if perm & 0o1 != 0o1 {
+                return Err(Error::new(EACCES));
+            }
+
+            //TODO: Only read elf header, not entire file. Then read required segments
+            data = vec![0; stat.st_size as usize];
+            syscall::file_op_mut_slice(syscall::number::SYS_READ, file.0, &mut data)?;
+            drop(file);
+
+            if data.starts_with(b"#!") {
+                if let Some(line) = data[2..].split(|&b| b == b'\n').next() {
+                    if ! args.is_empty() {
+                        args.remove(0);
+                    }
+                    args.insert(0, canonical);
+                    args.insert(0, line.to_vec());
+                    canonical = {
+                        let contexts = context::contexts();
+                        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+                        let context = context_lock.read();
+                        context.canonicalize(line)
+                    };
+                } else {
+                    println!("invalid script {}", unsafe { str::from_utf8_unchecked(path) });
+                    return Err(Error::new(ENOEXEC));
+                }
+            } else {
+                break;
+            }
+        }
 
         match elf::Elf::from(&data) {
             Ok(elf) => {
-- 
GitLab