Fix posix_spawn various issues

This commit is contained in:
Wildan M
2026-06-20 07:05:28 +07:00
parent fdcc92cd3b
commit 56d5fe6bce
5 changed files with 60 additions and 125 deletions
+27 -72
View File
@@ -5,12 +5,7 @@
mod file_actions;
mod spawn_attr;
use core::str::FromStr;
use alloc::{
ffi::CString,
string::{String, ToString},
};
use alloc::string::{String, ToString};
pub use file_actions::{Action, posix_spawn_file_actions_t};
pub use spawn_attr::{Flags, posix_spawnattr_t};
@@ -18,96 +13,56 @@ use crate::{
c_str::CStr,
error::{Errno, Result},
header::{
dirent::{opendir, readdir},
errno::{ENOENT, ENOTDIR},
limits::PATH_MAX,
errno,
stdlib::getenv,
unistd::{chdir, getcwd},
unistd::{F_OK, path::PathSearchIter},
},
iter::NulTerminated,
platform::{
self, ERRNO, Pal,
self, Pal, Sys,
types::{c_char, c_int, pid_t},
},
};
unsafe fn spawn(
pid: Option<&mut pid_t>,
mut program: String,
program: String,
file_actions: Option<&posix_spawn_file_actions_t>,
spawn_attr: Option<&posix_spawnattr_t>,
argv: NulTerminated<*mut c_char>,
envp: Option<NulTerminated<*mut c_char>>,
use_path: bool,
) -> Result<()> {
let mut original_cwd = [0 as c_char; PATH_MAX];
assert!(
unsafe { !getcwd(original_cwd.as_mut_ptr().cast::<c_char>(), PATH_MAX).is_null() },
"Error getting cwd: {}",
ERRNO.get()
);
let program = if use_path {
let error = errno::ENOENT;
let mut path_path = None;
if use_path {
let path = unsafe { getenv(c"PATH".as_ptr()) };
let path_env = unsafe { CStr::from_nullable_ptr(path).unwrap().to_str().unwrap() };
let path_elements = path_env.split(':');
let mut flag = false;
'a: for path_element in path_elements {
let pe = CString::from_str(path_element).unwrap();
let dir = if let Some(dir) =
unsafe { opendir(pe.as_bytes_with_nul().as_ptr().cast::<c_char>()).as_mut() }
{
dir
} else if ERRNO.get() == 0 || ERRNO.get() == ENOTDIR || ERRNO.get() == ENOENT {
continue;
} else {
return Err(Errno(ERRNO.get()));
};
while let Some(dir_ent) = unsafe { readdir(dir).as_ref() } {
let dir_ent_name = unsafe {
CStr::from_ptr(dir_ent.d_name.as_ptr().cast::<c_char>())
.to_str()
.unwrap()
.to_string()
};
if dir_ent_name == program {
flag = true;
program = format!("{}/{}", path_element, program);
path_path = Some(path_element.to_string());
break 'a;
let path_env = unsafe { getenv(c"PATH".as_ptr()) };
if !path_env.is_null() {
let path_env = unsafe { CStr::from_ptr(path_env) };
let fun = || {
for program_buf in PathSearchIter::new(&program.as_bytes(), &path_env) {
// SAFETY: CStr::from_ptr().to_bytes() always stop at null, no need to check again
let program_c =
unsafe { CStr::from_bytes_with_nul_unchecked(program_buf.as_slice()) };
if Sys::access(program_c, F_OK).is_ok() {
return program_c.to_str().ok().map(|s| s.to_string());
}
}
}
None
};
fun().ok_or(Errno { 0: error })?
} else {
return Err(Errno { 0: error });
}
if !flag {
return Err(Errno(ENOENT));
}
}
let program = CString::from_str(program.as_str()).unwrap();
} else {
program
};
unsafe {
platform::Sys::spawn(
CStr::from_bytes_with_nul(program.as_bytes_with_nul()).unwrap(),
file_actions,
spawn_attr,
argv,
envp,
path_path,
)
.map(|v| {
platform::Sys::spawn(program, file_actions, spawn_attr, argv, envp).map(|v| {
if let Some(pid) = pid {
*pid = v;
}
})
.inspect_err(|e| {
let status = chdir(original_cwd.as_ptr().cast::<c_char>());
if status != 0 {
panic!("Error switching back to original cwd: {}", ERRNO.get());
}
})?;
}