From fdcc92cd3b8bf2db8723e5593770d7e9c1c5089e Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sat, 20 Jun 2026 06:38:50 +0700 Subject: [PATCH] Add arrayvec and split PATH env logic --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/header/unistd/mod.rs | 24 +++++++-------------- src/header/unistd/path.rs | 44 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 src/header/unistd/path.rs diff --git a/Cargo.lock b/Cargo.lock index 47568e432f..816cb56e19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,12 @@ dependencies = [ "password-hash", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.5.1" @@ -484,6 +490,7 @@ name = "relibc" version = "0.2.5" dependencies = [ "argon2", + "arrayvec", "base64ct", "bcrypt-pbkdf", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 57dff8ebda..8de1648ac0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ cc = "1" [dependencies] bitflags.workspace = true +arrayvec = { version = "0.7.6", default-features = false } cbitset = "0.2" posix-regex = { version = "0.1.4", features = ["no_std"] } diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index 3bad48ebea..1d1d8171f0 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -25,7 +25,7 @@ use crate::{ sys_select::timeval, sys_time, sys_utsname, termios, time::timespec, - unistd::alarm::alarm_timespec, + unistd::{alarm::alarm_timespec, path::PathSearchIter}, }, out::Out, platform::{ @@ -57,6 +57,7 @@ mod alarm; mod brk; mod getopt; mod getpass; +pub mod path; mod pathconf; #[cfg(target_os = "linux")] pub mod syscall; @@ -369,16 +370,13 @@ pub unsafe extern "C" fn fexecve( .or_minus_one_errno() } -const PATH_SEPARATOR: u8 = b':'; - /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -> c_int { let file = unsafe { CStr::from_ptr(file) }; + let file_bytes = file.to_bytes(); - if file.to_bytes().contains(&b'/') - || (cfg!(target_os = "redox") && file.to_bytes().contains(&b':')) - { + if file_bytes.contains(&b'/') || (cfg!(target_os = "redox") && file_bytes.contains(&b':')) { unsafe { execv(file.as_ptr(), argv) } } else { let mut error = errno::ENOENT; @@ -386,16 +384,10 @@ pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) - let path_env = unsafe { getenv(c"PATH".as_ptr()) }; if !path_env.is_null() { let path_env = unsafe { CStr::from_ptr(path_env) }; - for path in path_env.to_bytes().split(|&b| b == PATH_SEPARATOR) { - let file = file.to_bytes(); - let length = file.len() + path.len() + 2; - let mut program = alloc::vec::Vec::with_capacity(length); - program.extend_from_slice(path); - program.push(b'/'); - program.extend_from_slice(file); - program.push(b'\0'); - - let program_c = CStr::from_bytes_with_nul(&program).unwrap(); + for program_buf in PathSearchIter::new(&file.to_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()) }; unsafe { execv(program_c.as_ptr(), argv) }; match platform::ERRNO.get() { diff --git a/src/header/unistd/path.rs b/src/header/unistd/path.rs new file mode 100644 index 0000000000..732280fae7 --- /dev/null +++ b/src/header/unistd/path.rs @@ -0,0 +1,44 @@ +use core::slice::Split; + +use arrayvec::ArrayVec; + +use crate::{c_str::CStr, header::limits::PATH_MAX}; + +pub struct PathSearchIter<'a> { + file_bytes: &'a [u8], + path_splits: Split<'a, u8, fn(&u8) -> bool>, +} + +const PATH_SEPARATOR: u8 = b':'; + +impl<'a> PathSearchIter<'a> { + /// Construct a new PATH parser. + /// Safety: file must have no slashes + pub fn new(file_bytes: &'a [u8], path_env: &'a CStr) -> Self { + Self { + file_bytes, + path_splits: path_env.to_bytes().split(|&b| b == PATH_SEPARATOR), + } + } +} + +impl<'a> Iterator for PathSearchIter<'a> { + type Item = ArrayVec; + + fn next(&mut self) -> Option { + for path in &mut self.path_splits { + let len = path.len() + self.file_bytes.len() + 2; + if len > PATH_MAX { + continue; + } + let mut program: ArrayVec = ArrayVec::new(); + program.try_extend_from_slice(path).unwrap(); + program.push(b'/'); + program.try_extend_from_slice(self.file_bytes).unwrap(); + program.push(b'\0'); + return Some(program); + } + + None + } +}