Skip to content

Commit 037c46f

Browse files
committed
Null terminated slices for exec
1 parent 9d912ae commit 037c46f

File tree

3 files changed

+111
-37
lines changed

3 files changed

+111
-37
lines changed

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub mod libc {
3232
pub use libc::{c_int, c_void};
3333
pub use errno::Errno;
3434

35+
pub mod null_terminated;
36+
3537
pub mod errno;
3638
pub mod features;
3739
pub mod fcntl;

src/null_terminated.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::ops::{Deref, DerefMut};
2+
use std::ffi::CStr;
3+
use std::mem::transmute;
4+
use std::iter;
5+
use libc::c_char;
6+
7+
pub struct NullTerminatedSlice<T> {
8+
inner: [Option<T>],
9+
}
10+
11+
impl<T> NullTerminatedSlice<T> {
12+
pub fn from_slice(slice: &[Option<T>]) -> Option<&Self> {
13+
if slice.last().map(Option::is_none).unwrap_or(false) {
14+
Some(unsafe { Self::from_slice_unchecked(slice) })
15+
} else {
16+
None
17+
}
18+
}
19+
20+
pub fn from_slice_mut(slice: &mut [Option<T>]) -> Option<&mut Self> {
21+
if slice.last().map(Option::is_none).unwrap_or(false) {
22+
Some(unsafe { Self::from_slice_mut_unchecked(slice) })
23+
} else {
24+
None
25+
}
26+
}
27+
28+
pub unsafe fn from_slice_unchecked(slice: &[Option<T>]) -> &Self {
29+
transmute(slice)
30+
}
31+
32+
pub unsafe fn from_slice_mut_unchecked(slice: &mut [Option<T>]) -> &mut Self {
33+
transmute(slice)
34+
}
35+
}
36+
37+
impl<'a, U: Sized> NullTerminatedSlice<&'a U> {
38+
pub fn as_ptr(&self) -> *const *const U {
39+
self.inner.as_ptr() as *const _
40+
}
41+
}
42+
43+
impl<T> Deref for NullTerminatedSlice<T> {
44+
type Target = [Option<T>];
45+
46+
fn deref(&self) -> &Self::Target {
47+
&self.inner[..self.inner.len() - 1]
48+
}
49+
}
50+
51+
impl<T> DerefMut for NullTerminatedSlice<T> {
52+
fn deref_mut(&mut self) -> &mut Self::Target {
53+
let len = self.inner.len();
54+
&mut self.inner[..len - 1]
55+
}
56+
}
57+
58+
pub trait BorrowNullTerminatedSlice<T> {
59+
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&T>) -> R>(self, f: F) -> R;
60+
}
61+
62+
impl<T: AsRef<CStr>, I: IntoIterator<Item=T>> BorrowNullTerminatedSlice<c_char> for I {
63+
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&c_char>) -> R>(self, f: F) -> R {
64+
fn cstr_char<'a, S: AsRef<CStr> + 'a>(s: S) -> &'a c_char {
65+
unsafe {
66+
&*s.as_ref().as_ptr()
67+
}
68+
}
69+
70+
let values: Vec<_> = self.into_iter()
71+
.map(cstr_char)
72+
.map(Some).chain(iter::once(None)).collect();
73+
let terminated = unsafe { NullTerminatedSlice::from_slice_unchecked(&values[..]) };
74+
75+
f(terminated)
76+
}
77+
}
78+
79+
impl<'a, T: 'a> BorrowNullTerminatedSlice<T> for &'a NullTerminatedSlice<&'a T> {
80+
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&T>) -> R>(self, f: F) -> R {
81+
f(self)
82+
}
83+
}

src/unistd.rs

+26-37
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//! Standard symbolic constants and types
22
//!
33
use {Errno, Error, Result, NixPath};
4+
use null_terminated::BorrowNullTerminatedSlice;
45
use fcntl::{fcntl, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC};
56
use fcntl::FcntlArg::{F_SETFD, F_SETFL};
67
use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t};
78
use std::mem;
8-
use std::ffi::CString;
9+
use std::ffi::CStr;
910
use std::os::unix::io::RawFd;
1011

1112
#[cfg(any(target_os = "linux", target_os = "android"))]
@@ -120,47 +121,39 @@ pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<uid_t>, group: Option<
120121
Errno::result(res).map(drop)
121122
}
122123

123-
fn to_exec_array(args: &[CString]) -> Vec<*const c_char> {
124-
use std::ptr;
125-
use libc::c_char;
126-
127-
let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
128-
args_p.push(ptr::null());
129-
args_p
130-
}
131-
132124
#[inline]
133-
pub fn execv(path: &CString, argv: &[CString]) -> Result<()> {
134-
let args_p = to_exec_array(argv);
135-
136-
unsafe {
137-
libc::execv(path.as_ptr(), args_p.as_ptr())
138-
};
125+
pub fn execv<A: BorrowNullTerminatedSlice<c_char>>(path: &CStr, argv: A) -> Result<()> {
126+
argv.borrow_null_terminated_slice(|args_p| {
127+
unsafe {
128+
libc::execv(path.as_ptr(), args_p.as_ptr())
129+
};
139130

140-
Err(Error::Sys(Errno::last()))
131+
Err(Error::Sys(Errno::last()))
132+
})
141133
}
142134

143135
#[inline]
144-
pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<()> {
145-
let args_p = to_exec_array(args);
146-
let env_p = to_exec_array(env);
147-
148-
unsafe {
149-
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
150-
};
151-
152-
Err(Error::Sys(Errno::last()))
136+
pub fn execve<A: BorrowNullTerminatedSlice<c_char>, E: BorrowNullTerminatedSlice<c_char>>(path: &CStr, args: A, env: E) -> Result<()> {
137+
args.borrow_null_terminated_slice(|args_p| {
138+
env.borrow_null_terminated_slice(|env_p| {
139+
unsafe {
140+
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
141+
};
142+
143+
Err(Error::Sys(Errno::last()))
144+
})
145+
})
153146
}
154147

155148
#[inline]
156-
pub fn execvp(filename: &CString, args: &[CString]) -> Result<()> {
157-
let args_p = to_exec_array(args);
158-
159-
unsafe {
160-
libc::execvp(filename.as_ptr(), args_p.as_ptr())
161-
};
149+
pub fn execvp<A: BorrowNullTerminatedSlice<c_char>>(filename: &CStr, args: A) -> Result<()> {
150+
args.borrow_null_terminated_slice(|args_p| {
151+
unsafe {
152+
libc::execvp(filename.as_ptr(), args_p.as_ptr())
153+
};
162154

163-
Err(Error::Sys(Errno::last()))
155+
Err(Error::Sys(Errno::last()))
156+
})
164157
}
165158

166159
pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
@@ -378,9 +371,6 @@ mod linux {
378371
use sys::syscall::{syscall, SYSPIVOTROOT};
379372
use {Errno, Result, NixPath};
380373

381-
#[cfg(feature = "execvpe")]
382-
use std::ffi::CString;
383-
384374
pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
385375
new_root: &P1, put_old: &P2) -> Result<()> {
386376
let res = try!(try!(new_root.with_nix_path(|new_root| {
@@ -394,7 +384,6 @@ mod linux {
394384
Errno::result(res).map(drop)
395385
}
396386

397-
#[inline]
398387
#[cfg(feature = "execvpe")]
399388
pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<()> {
400389
use std::ptr;

0 commit comments

Comments
 (0)