use alloc::string::String; #[allow(unused_imports)] use alloc::string::ToString; use alloc::sync::Arc; use crate::arch::interrupt::TrapFrame; use crate::arch::syscall::nr::SYS_EXECVE; use crate::filesystem::vfs::{IndexNode, MAX_PATHLEN}; use crate::mm::page::PAGE_4K_SIZE; use crate::mm::{verify_area, VirtAddr}; use crate::process::execve::do_execve; use crate::process::{ProcessControlBlock, ProcessManager}; use crate::syscall::table::{FormattedSyscallParam, Syscall}; use crate::syscall::user_access::{check_and_clone_cstr, check_and_clone_cstr_array}; use alloc::{ffi::CString, vec::Vec}; use log::error; use system_error::SystemError; pub struct SysExecve; impl SysExecve { fn path_ptr(args: &[usize]) -> usize { args[0] } fn argv_ptr(args: &[usize]) -> usize { args[1] } fn env_ptr(args: &[usize]) -> usize { args[2] } pub fn check_args( frame: &mut TrapFrame, path_ptr: usize, argv_ptr: usize, env_ptr: usize, ) -> Result<(), SystemError> { if path_ptr == 0 { return Err(SystemError::EINVAL); } let virt_path_ptr = VirtAddr::new(path_ptr); let virt_argv_ptr = VirtAddr::new(argv_ptr); let virt_env_ptr = VirtAddr::new(env_ptr); // 权限校验 if frame.is_from_user() && (verify_area(virt_path_ptr, MAX_PATHLEN).is_err() || verify_area(virt_argv_ptr, PAGE_4K_SIZE).is_err()) || verify_area(virt_env_ptr, PAGE_4K_SIZE).is_err() { return Err(SystemError::EFAULT); } Ok(()) } pub fn basic_args( path: *const u8, argv: *const *const u8, envp: *const *const u8, ) -> Result<(CString, Vec, Vec), SystemError> { let path: CString = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; #[cfg(not(feature = "initram"))] let argv: Vec = check_and_clone_cstr_array(argv)?; #[cfg(feature = "initram")] let mut argv: Vec = check_and_clone_cstr_array(argv)?; let envp: Vec = check_and_clone_cstr_array(envp)?; // 这里需要处理符号链接, 目前内核没有完整实现, 这里是个简易的替代 // 例如执行/bin/echo, 必须拿到echo这个名字, 目前内核只有硬链接, 会执行/bin/busybox, 导致无法识别命令 #[cfg(feature = "initram")] { let real = crate::filesystem::vfs::get_link_true_file(argv[0].to_string_lossy().to_string()) .unwrap(); argv[0] = CString::new(real).unwrap(); } Ok((path, argv, envp)) } pub fn execve( inode: Arc, path: String, argv: Vec, envp: Vec, frame: &mut TrapFrame, ) -> Result<(), SystemError> { ProcessManager::current_pcb() .basic_mut() .set_name(ProcessControlBlock::generate_name(&path)); do_execve(inode, argv, envp, frame)?; let pcb = ProcessManager::current_pcb(); // 关闭设置了O_CLOEXEC的文件描述符 let fd_table = pcb.fd_table(); fd_table.write().close_on_exec(); pcb.set_execute_path(path); Ok(()) } } impl Syscall for SysExecve { fn num_args(&self) -> usize { 3 } fn handle(&self, args: &[usize], frame: &mut TrapFrame) -> Result { let path_ptr = Self::path_ptr(args); let argv_ptr = Self::argv_ptr(args); let env_ptr = Self::env_ptr(args); Self::check_args(frame, path_ptr, argv_ptr, env_ptr)?; let (path, argv, envp) = Self::basic_args( path_ptr as *const u8, argv_ptr as *const *const u8, env_ptr as *const *const u8, ) .inspect_err(|e: &SystemError| { error!("Failed to execve: {:?}", e); })?; let path = path.into_string().map_err(|_| SystemError::EINVAL)?; let pwd = ProcessManager::current_pcb().pwd_inode(); let inode = pwd.lookup(&path)?; Self::execve(inode, path, argv, envp, frame)?; return Ok(0); } fn entry_format(&self, args: &[usize]) -> Vec { vec![ FormattedSyscallParam::new("path", format!("{:#x}", Self::path_ptr(args))), FormattedSyscallParam::new("argv", format!("{:#x}", Self::argv_ptr(args))), FormattedSyscallParam::new("env", format!("{:#x}", Self::env_ptr(args))), ] } } syscall_table_macros::declare_syscall!(SYS_EXECVE, SysExecve);