Przeglądaj źródła

feat(execve): support the shebang scripts

greatbridf 7 miesięcy temu
rodzic
commit
56f11a3175
3 zmienionych plików z 85 dodań i 25 usunięć
  1. 5 7
      src/kernel/syscall/procops.rs
  2. 73 11
      src/kernel/task/loader/mod.rs
  3. 7 7
      src/lib.rs

+ 5 - 7
src/kernel/syscall/procops.rs

@@ -151,22 +151,20 @@ fn get_strings(mut ptr_strings: UserPointer<'_, PtrT>) -> KResult<Vec<CString>>
 #[eonix_macros::define_syscall(SYS_EXECVE)]
 fn execve(exec: *const u8, argv: *const PtrT, envp: *const PtrT) -> KResult<SyscallNoReturn> {
     let exec = UserString::new(exec)?;
+    let exec = exec.as_cstr().to_owned();
+
     let argv = get_strings(UserPointer::new(argv)?)?;
     let envp = get_strings(UserPointer::new(envp)?)?;
 
-    let dentry = Dentry::open(
-        &thread.fs_context,
-        Path::new(exec.as_cstr().to_bytes())?,
-        true,
-    )?;
-
+    let dentry = Dentry::open(&thread.fs_context, Path::new(exec.as_bytes())?, true)?;
     if !dentry.is_valid() {
         Err(ENOENT)?;
     }
 
     // TODO: When `execve` is called by one of the threads in a process, the other threads
     //       should be terminated and `execve` is performed in the thread group leader.
-    let load_info = ProgramLoader::parse(dentry.clone())?.load(argv, envp)?;
+    let load_info =
+        ProgramLoader::parse(&thread.fs_context, exec, dentry.clone(), argv, envp)?.load()?;
 
     if let Some(robust_list) = thread.get_robust_list() {
         let _ = Task::block_on(robust_list.wake_all());

+ 73 - 11
src/kernel/task/loader/mod.rs

@@ -1,11 +1,12 @@
 use crate::io::ByteBuffer;
 use crate::kernel::constants::ENOEXEC;
 use crate::kernel::task::loader::elf::ELF;
+use crate::kernel::vfs::FsContext;
+use crate::path::Path;
 use crate::{
     kernel::{mem::MMList, vfs::dentry::Dentry},
     prelude::*,
 };
-
 use alloc::{ffi::CString, sync::Arc};
 use eonix_mm::address::VAddr;
 
@@ -26,27 +27,88 @@ enum Object {
 }
 
 pub struct ProgramLoader {
+    args: Vec<CString>,
+    envs: Vec<CString>,
     object: Object,
 }
 
 impl ProgramLoader {
-    pub fn parse(file: Arc<Dentry>) -> KResult<Self> {
-        let mut magic = [0u8; 4];
-        file.read(&mut ByteBuffer::new(magic.as_mut_slice()), 0)?;
+    pub fn parse(
+        fs_context: &FsContext,
+        mut exec_path: CString,
+        mut file: Arc<Dentry>,
+        mut args: Vec<CString>,
+        envs: Vec<CString>,
+    ) -> KResult<Self> {
+        const RECURSION_LIMIT: usize = 4;
+        let mut nrecur = 0;
+
+        let object = loop {
+            if nrecur >= RECURSION_LIMIT {
+                return Err(ENOEXEC);
+            }
+
+            let mut magic = [0; 4];
+            file.read(&mut ByteBuffer::new(magic.as_mut_slice()), 0)?;
+
+            match magic {
+                [b'#', b'!', ..] => {
+                    let mut interpreter_line = [0; 256];
+                    let nread = file.read(&mut ByteBuffer::new(&mut interpreter_line), 0)?;
+
+                    // There is a tiny time gap between reading the magic number and
+                    // reading the interpreter line, so we need to check if the line
+                    // still starts with a shebang.
+                    if !interpreter_line.starts_with(b"#!") {
+                        return Err(ENOEXEC);
+                    }
+
+                    let line = &interpreter_line[2..nread];
+                    let line = line.split(|&b| b == b'\n' || b == b'\0').next().unwrap();
+
+                    let interpreter_name;
+                    let mut interpreter_arg = None;
+                    match line.iter().position(|&b| b == b' ') {
+                        Some(blank_pos) => {
+                            interpreter_name = CString::new(&line[..blank_pos]).unwrap();
+                            interpreter_arg = Some(CString::new(&line[blank_pos + 1..]).unwrap());
+                        }
+                        None => interpreter_name = CString::new(line).unwrap(),
+                    }
+
+                    let path = Path::new(interpreter_name.as_bytes())?;
+                    file = Dentry::open(fs_context, path, true)?;
+
+                    args.insert(0, interpreter_name.clone());
+                    if let Some(arg) = interpreter_arg {
+                        args.insert(1, arg);
+                    }
+
+                    if args.len() > 2 {
+                        args[2] = exec_path;
+                    } else {
+                        args.push(exec_path);
+                    }
+
+                    exec_path = interpreter_name;
+                }
+                ELF_MAGIC => break ELF::parse(file)?,
+                _ => return Err(ENOEXEC),
+            }
 
-        let object = match magic {
-            ELF_MAGIC => ELF::parse(file),
-            _ => Err(ENOEXEC),
-        }?;
+            nrecur += 1;
+        };
 
         Ok(ProgramLoader {
+            args,
+            envs,
             object: Object::ELF(object),
         })
     }
 
-    pub fn load(&self, args: Vec<CString>, envs: Vec<CString>) -> KResult<LoadInfo> {
-        match &self.object {
-            Object::ELF(elf) => elf.load(args, envs),
+    pub fn load(self) -> KResult<LoadInfo> {
+        match self.object {
+            Object::ELF(elf) => elf.load(self.args, self.envs),
         }
     }
 }

+ 7 - 7
src/lib.rs

@@ -172,10 +172,10 @@ async fn init_process(early_kstack: PRange) {
 
         let mut init_name = None;
         let mut init = None;
-        for name in &init_names {
+        for name in init_names {
             if let Ok(dentry) = Dentry::open(fs_context, Path::new(name).unwrap(), true) {
                 if dentry.is_valid() {
-                    init_name = Some(*name);
+                    init_name = Some(CString::new(name).unwrap());
                     init = Some(dentry);
                     break;
                 }
@@ -186,7 +186,7 @@ async fn init_process(early_kstack: PRange) {
         let init_name = init_name.unwrap();
 
         let argv = vec![
-            CString::new(init_name).unwrap(),
+            init_name.clone(),
             CString::new("sh").unwrap(),
             CString::new("/mnt/initsh").unwrap(),
         ];
@@ -198,10 +198,10 @@ async fn init_process(early_kstack: PRange) {
             CString::new("PWD=/").unwrap(),
         ];
 
-        ProgramLoader::parse(init.clone())
-            .unwrap()
-            .load(argv, envp)
-            .unwrap()
+        ProgramLoader::parse(fs_context, init_name, init.clone(), argv, envp)
+            .expect("Failed to parse init program")
+            .load()
+            .expect("Failed to load init program")
     };
 
     let thread_builder = ThreadBuilder::new()