Add support for executing a script that begins with a shebang
This commit is contained in:
parent
ec970f0e76
commit
92207d5535
85
src/libos/src/process/do_spawn/exec_loader.rs
Normal file
85
src/libos/src/process/do_spawn/exec_loader.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use super::ThreadRef;
|
||||
use crate::fs::{FileMode, INodeExt};
|
||||
use crate::prelude::*;
|
||||
use rcore_fs::vfs::INode;
|
||||
use std::ffi::CString;
|
||||
|
||||
/// Load an ELF file itself or a script's interpreter into a vector.
|
||||
///
|
||||
/// If the file is an executable binary, then just load this file.
|
||||
/// If the file is an script text, then parse the shebang and load
|
||||
/// the interpreter.
|
||||
pub fn load_exec_file_to_vec(
|
||||
file_path: &str,
|
||||
current_ref: &ThreadRef,
|
||||
) -> Result<(Option<String>, Vec<u8>)> {
|
||||
let file_buf = load_file_to_vec(&file_path, current_ref)?;
|
||||
let is_script = is_script_file(&file_buf);
|
||||
if is_script {
|
||||
// load interpreter
|
||||
let interpreter_path = parse_script_interpreter(&file_buf)?;
|
||||
if interpreter_path.starts_with("/host/") {
|
||||
return_errno!(
|
||||
EACCES,
|
||||
"libos doesn't support executing binaries from \"/host\" directory"
|
||||
);
|
||||
}
|
||||
let elf_buf = load_file_to_vec(&interpreter_path, current_ref)?;
|
||||
Ok((Some(interpreter_path), elf_buf))
|
||||
} else {
|
||||
Ok((None, file_buf))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_script_file(file_buf: &Vec<u8>) -> bool {
|
||||
file_buf.starts_with(&[b'#', b'!'])
|
||||
}
|
||||
|
||||
// TODO: Support parsing interpreter arguments. e.g. `/usr/bin/python -u`
|
||||
fn parse_script_interpreter(file_buf: &Vec<u8>) -> Result<String> {
|
||||
let mut start = 2; // after '#', '!'
|
||||
const MAX_LEN: usize = 127;
|
||||
|
||||
// skip whitespaced between shebang and interpreter
|
||||
while (start < file_buf.len())
|
||||
&& (file_buf[start] == ' ' as u8 || file_buf[start] == '\t' as u8)
|
||||
{
|
||||
start += 1;
|
||||
}
|
||||
|
||||
let end = file_buf
|
||||
.iter()
|
||||
.take(MAX_LEN)
|
||||
.position(|&c| c == '\n' as u8)
|
||||
.ok_or_else(|| errno!(EINVAL, "script parsing error"))?;
|
||||
|
||||
let interpreter = std::str::from_utf8(&file_buf[start..end])
|
||||
.map_err(|e| errno!(ENOEXEC, "failed to get the script interpreter"))?;
|
||||
trace!("script file using interpreter: {:?}", interpreter);
|
||||
Ok(interpreter.to_owned())
|
||||
}
|
||||
|
||||
pub fn load_file_to_vec(file_path: &str, current_ref: &ThreadRef) -> Result<Vec<u8>> {
|
||||
let inode = current_ref
|
||||
.fs()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.lookup_inode_follow(file_path)
|
||||
.map_err(|e| errno!(e.errno(), "cannot find the file"))?;
|
||||
let file_mode = {
|
||||
let info = inode.metadata()?;
|
||||
FileMode::from_bits_truncate(info.mode)
|
||||
};
|
||||
if !file_mode.is_executable() {
|
||||
return_errno!(EACCES, "file is not executable");
|
||||
}
|
||||
if file_mode.has_set_uid() || file_mode.has_set_gid() {
|
||||
warn!(
|
||||
"set-user-ID and set-group-ID are not supportted, FileMode:{:?}",
|
||||
file_mode
|
||||
);
|
||||
}
|
||||
inode
|
||||
.read_as_vec()
|
||||
.map_err(|e| errno!(e.errno(), "failed to read the file"))
|
||||
}
|
@ -2,18 +2,20 @@ use std::ffi::{CStr, CString};
|
||||
use std::path::Path;
|
||||
|
||||
use self::aux_vec::{AuxKey, AuxVec};
|
||||
use self::exec_loader::{load_exec_file_to_vec, load_file_to_vec};
|
||||
use super::elf_file::{ElfFile, ElfHeader, ProgramHeader, ProgramHeaderExt};
|
||||
use super::process::ProcessBuilder;
|
||||
use super::task::Task;
|
||||
use super::{table, task, ProcessRef, ThreadRef};
|
||||
use crate::fs::{
|
||||
CreationFlags, File, FileDesc, FileMode, FileTable, FsView, HostStdioFds, INodeExt, StdinFile,
|
||||
StdoutFile, ROOT_INODE,
|
||||
CreationFlags, File, FileDesc, FileTable, FsView, HostStdioFds, StdinFile, StdoutFile,
|
||||
ROOT_INODE,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::vm::ProcessVM;
|
||||
|
||||
mod aux_vec;
|
||||
mod exec_loader;
|
||||
mod init_stack;
|
||||
mod init_vm;
|
||||
|
||||
@ -91,17 +93,31 @@ fn do_spawn_common(
|
||||
|
||||
/// Create a new process and its main thread.
|
||||
fn new_process(
|
||||
elf_path: &str,
|
||||
file_path: &str,
|
||||
argv: &[CString],
|
||||
envp: &[CString],
|
||||
file_actions: &[FileAction],
|
||||
host_stdio_fds: Option<&HostStdioFds>,
|
||||
current_ref: &ThreadRef,
|
||||
) -> Result<ProcessRef> {
|
||||
let elf_buf = load_elf_to_vec(elf_path, current_ref)
|
||||
.cause_err(|e| errno!(e.errno(), "cannot load the executable"))?;
|
||||
let mut argv = argv.clone().to_vec();
|
||||
let (is_script, elf_buf) = load_exec_file_to_vec(file_path, current_ref)?;
|
||||
|
||||
// elf_path might be different from file_path because file_path could lead to a script text file.
|
||||
// And intepreter will be the loaded ELF.
|
||||
let elf_path = if let Some(interpreter_path) = is_script {
|
||||
if argv.len() == 0 {
|
||||
return_errno!(EINVAL, "argv[0] not found");
|
||||
}
|
||||
argv.insert(0, CString::new(interpreter_path.as_str())?);
|
||||
argv[1] = CString::new(file_path)?; // script file needs to be the full path
|
||||
interpreter_path
|
||||
} else {
|
||||
file_path.to_string()
|
||||
};
|
||||
|
||||
let ldso_path = "/lib/ld-musl-x86_64.so.1";
|
||||
let ldso_elf_buf = load_elf_to_vec(ldso_path, current_ref)
|
||||
let ldso_elf_buf = load_file_to_vec(ldso_path, current_ref)
|
||||
.cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?;
|
||||
|
||||
let exec_elf_file =
|
||||
@ -145,7 +161,7 @@ fn new_process(
|
||||
};
|
||||
let user_stack_base = vm.get_stack_base();
|
||||
let user_stack_limit = vm.get_stack_limit();
|
||||
let user_rsp = init_stack::do_init(user_stack_base, 4096, argv, envp, &auxvec)?;
|
||||
let user_rsp = init_stack::do_init(user_stack_base, 4096, &argv, envp, &auxvec)?;
|
||||
unsafe {
|
||||
Task::new(
|
||||
ldso_entry,
|
||||
@ -166,7 +182,7 @@ fn new_process(
|
||||
|
||||
ProcessBuilder::new()
|
||||
.vm(vm_ref)
|
||||
.exec_path(elf_path)
|
||||
.exec_path(&elf_path)
|
||||
.parent(process_ref)
|
||||
.task(task)
|
||||
.sched(sched_ref)
|
||||
@ -201,31 +217,6 @@ pub enum FileAction {
|
||||
Close(FileDesc),
|
||||
}
|
||||
|
||||
fn load_elf_to_vec(elf_path: &str, current_ref: &ThreadRef) -> Result<Vec<u8>> {
|
||||
let inode = current_ref
|
||||
.fs()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.lookup_inode_follow(elf_path)
|
||||
.map_err(|e| errno!(e.errno(), "cannot find the ELF"))?;
|
||||
let file_mode = {
|
||||
let info = inode.metadata()?;
|
||||
FileMode::from_bits_truncate(info.mode)
|
||||
};
|
||||
if !file_mode.is_executable() {
|
||||
return_errno!(EACCES, "elf file is not executable");
|
||||
}
|
||||
if file_mode.has_set_uid() || file_mode.has_set_gid() {
|
||||
warn!(
|
||||
"set-user-ID and set-group-ID are not supportted, FileMode:{:?}",
|
||||
file_mode
|
||||
);
|
||||
}
|
||||
inode
|
||||
.read_as_vec()
|
||||
.map_err(|e| errno!(e.errno(), "failed to read the executable ELF"))
|
||||
}
|
||||
|
||||
fn init_files(
|
||||
current_ref: &ThreadRef,
|
||||
file_actions: &[FileAction],
|
||||
|
Loading…
Reference in New Issue
Block a user