From 7cd7bb5f6130a6720a12c0026ab16f1961857ca9 Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Mon, 15 Oct 2018 21:16:04 +0200
Subject: [PATCH] Interpret shebangs on redox

This is no longer handled on the kernel side
---
 src/libstd/sys/redox/process.rs | 88 ++++++++++++++++++++++++++++-----
 1 file changed, 76 insertions(+), 12 deletions(-)

diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs
index c9cee52835ca..340c03a213af 100644
--- a/src/libstd/sys/redox/process.rs
+++ b/src/libstd/sys/redox/process.rs
@@ -10,15 +10,17 @@
 
 use env::{split_paths};
 use ffi::{CStr, OsStr};
+use fs::File;
 use os::unix::ffi::OsStrExt;
 use fmt;
-use io::{self, Error, ErrorKind};
-use iter;
+use io::{self, prelude::*, BufReader, Error, ErrorKind, SeekFrom};
 use libc::{EXIT_SUCCESS, EXIT_FAILURE};
 use path::{Path, PathBuf};
 use ptr;
+use sys::ext::fs::MetadataExt;
+use sys::ext::io::AsRawFd;
 use sys::fd::FileDesc;
-use sys::fs::{File, OpenOptions};
+use sys::fs::{File as SysFile, OpenOptions};
 use sys::os::{ENV_LOCK, environ};
 use sys::pipe::{self, AnonPipe};
 use sys::{cvt, syscall};
@@ -315,22 +317,85 @@ macro_rules! t {
             None
         };
 
-        let fd = if let Some(program) = program_opt {
-            t!(cvt(syscall::open(program.as_os_str().as_bytes(), syscall::O_RDONLY | syscall::O_CLOEXEC)))
+        let mut file = if let Some(program) = program_opt {
+            t!(File::open(program.as_os_str()))
         } else {
             return io::Error::from_raw_os_error(syscall::ENOENT);
         };
 
         self.env.apply();
 
-        let mut args: Vec<[usize; 2]> = Vec::new();
+        // Push all the arguments
+        let mut args: Vec<[usize; 2]> = Vec::with_capacity(1 + self.args.len());
+
+        let interpreter = {
+            let mut reader = BufReader::new(&file);
+
+            let mut shebang = [0; 2];
+            let mut read = 0;
+            loop {
+                match t!(reader.read(&mut shebang[read..])) {
+                    0 => break,
+                    n => read += n,
+                }
+            }
+
+            if &shebang == b"#!" {
+                // This is an interpreted script.
+                // First of all, since we'll be passing another file to
+                // fexec(), we need to manually check that we have permission
+                // to execute this file:
+                let uid = t!(cvt(syscall::getuid()));
+                let gid = t!(cvt(syscall::getgid()));
+                let meta = t!(file.metadata());
+
+                let mode = if uid == meta.uid() as usize {
+                    meta.mode() >> 3*2 & 0o7
+                } else if gid == meta.gid() as usize {
+                    meta.mode() >> 3*1 & 0o7
+                } else {
+                    meta.mode() & 0o7
+                };
+                if mode & 1 == 0 {
+                    return io::Error::from_raw_os_error(syscall::EPERM);
+                }
+
+                // Second of all, we need to actually read which interpreter it wants
+                let mut interpreter = Vec::new();
+                t!(reader.read_until(b'\n', &mut interpreter));
+                // Pop one trailing newline, if any
+                if interpreter.ends_with(&[b'\n']) {
+                    interpreter.pop().unwrap();
+                }
+
+                // TODO: Here we could just reassign `file` directly, if it
+                // wasn't for lexical lifetimes. Remove the whole `let
+                // interpreter = { ... };` hack once NLL lands.
+                // NOTE: Although DO REMEMBER to make sure the interpreter path
+                // still lives long enough to reach fexec.
+                Some(interpreter)
+            } else {
+                None
+            }
+        };
+        if let Some(ref interpreter) = interpreter {
+            let path: &OsStr = OsStr::from_bytes(&interpreter);
+            file = t!(File::open(path));
+
+            args.push([interpreter.as_ptr() as usize, interpreter.len()]);
+        } else {
+            t!(file.seek(SeekFrom::Start(0)));
+        }
+
         args.push([self.program.as_ptr() as usize, self.program.len()]);
+
         for arg in self.args.iter() {
             args.push([arg.as_ptr() as usize, arg.len()]);
         }
 
+        // Push all the variables
         let mut vars: Vec<[usize; 2]> = Vec::new();
-        unsafe {
+        {
             let _guard = ENV_LOCK.lock();
             let mut environ = *environ();
             while *environ != ptr::null() {
@@ -340,8 +405,7 @@ macro_rules! t {
             }
         }
 
-        if let Err(err) = syscall::fexec(fd, &args, &vars) {
-            let _ = syscall::close(fd);
+        if let Err(err) = syscall::fexec(file.as_raw_fd(), &args, &vars) {
             io::Error::from_raw_os_error(err.errno as i32)
         } else {
             panic!("return from exec without err");
@@ -408,7 +472,7 @@ fn to_child_stdio(&self, readable: bool)
                 let mut opts = OpenOptions::new();
                 opts.read(readable);
                 opts.write(!readable);
-                let fd = File::open(Path::new("null:"), &opts)?;
+                let fd = SysFile::open(Path::new("null:"), &opts)?;
                 Ok((ChildStdio::Owned(fd.into_fd()), None))
             }
         }
@@ -421,8 +485,8 @@ fn from(pipe: AnonPipe) -> Stdio {
     }
 }
 
-impl From<File> for Stdio {
-    fn from(file: File) -> Stdio {
+impl From<SysFile> for Stdio {
+    fn from(file: SysFile) -> Stdio {
         Stdio::Fd(file.into_fd())
     }
 }
-- 
GitLab