Skip to content

Commit f4ecc1f

Browse files
authored
Rollup merge of rust-lang#55182 - jD91mZM2:rebased, r=alexcrichton
Redox: Update to new changes These are all cherry-picked from our fork: - Remove the `env:` scheme - Update `execve` system call to `fexec` - Interpret shebangs: these are no longer handled by the kernel, which like usual tries to be as minimal as possible
2 parents 4ec0ba9 + 51e2a63 commit f4ecc1f

File tree

7 files changed

+236
-64
lines changed

7 files changed

+236
-64
lines changed

src/libstd/sys/redox/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,29 @@ pub fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
7777
result.map_err(|err| io::Error::from_raw_os_error(err.errno))
7878
}
7979

80+
#[doc(hidden)]
81+
pub trait IsMinusOne {
82+
fn is_minus_one(&self) -> bool;
83+
}
84+
85+
macro_rules! impl_is_minus_one {
86+
($($t:ident)*) => ($(impl IsMinusOne for $t {
87+
fn is_minus_one(&self) -> bool {
88+
*self == -1
89+
}
90+
})*)
91+
}
92+
93+
impl_is_minus_one! { i8 i16 i32 i64 isize }
94+
95+
pub fn cvt_libc<T: IsMinusOne>(t: T) -> io::Result<T> {
96+
if t.is_minus_one() {
97+
Err(io::Error::last_os_error())
98+
} else {
99+
Ok(t)
100+
}
101+
}
102+
80103
/// On Redox, use an illegal instruction to abort
81104
pub unsafe fn abort_internal() -> ! {
82105
::core::intrinsics::abort();

src/libstd/sys/redox/os.rs

+68-33
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
1313
#![allow(unused_imports)] // lots of cfg code here
1414

15+
use libc::{self, c_char};
16+
1517
use os::unix::prelude::*;
1618

1719
use error::Error as StdError;
18-
use ffi::{OsString, OsStr};
20+
use ffi::{CStr, CString, OsStr, OsString};
1921
use fmt;
2022
use io::{self, Read, Write};
2123
use iter;
@@ -27,7 +29,7 @@ use ptr;
2729
use slice;
2830
use str;
2931
use sys_common::mutex::Mutex;
30-
use sys::{cvt, fd, syscall};
32+
use sys::{cvt, cvt_libc, fd, syscall};
3133
use vec;
3234

3335
extern {
@@ -129,6 +131,8 @@ pub fn current_exe() -> io::Result<PathBuf> {
129131
Ok(PathBuf::from(path))
130132
}
131133

134+
pub static ENV_LOCK: Mutex = Mutex::new();
135+
132136
pub struct Env {
133137
iter: vec::IntoIter<(OsString, OsString)>,
134138
_dont_send_or_sync_me: PhantomData<*mut ()>,
@@ -140,52 +144,83 @@ impl Iterator for Env {
140144
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
141145
}
142146

147+
pub unsafe fn environ() -> *mut *const *const c_char {
148+
extern { static mut environ: *const *const c_char; }
149+
&mut environ
150+
}
151+
143152
/// Returns a vector of (variable, value) byte-vector pairs for all the
144153
/// environment variables of the current process.
145154
pub fn env() -> Env {
146-
let mut variables: Vec<(OsString, OsString)> = Vec::new();
147-
if let Ok(mut file) = ::fs::File::open("env:") {
148-
let mut string = String::new();
149-
if file.read_to_string(&mut string).is_ok() {
150-
for line in string.lines() {
151-
let mut parts = line.splitn(2, '=');
152-
if let Some(name) = parts.next() {
153-
let value = parts.next().unwrap_or("");
154-
variables.push((OsString::from(name.to_string()),
155-
OsString::from(value.to_string())));
156-
}
155+
unsafe {
156+
let _guard = ENV_LOCK.lock();
157+
let mut environ = *environ();
158+
if environ == ptr::null() {
159+
panic!("os::env() failure getting env string from OS: {}",
160+
io::Error::last_os_error());
161+
}
162+
let mut result = Vec::new();
163+
while *environ != ptr::null() {
164+
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
165+
result.push(key_value);
157166
}
167+
environ = environ.offset(1);
168+
}
169+
return Env {
170+
iter: result.into_iter(),
171+
_dont_send_or_sync_me: PhantomData,
172+
}
173+
}
174+
175+
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
176+
// Strategy (copied from glibc): Variable name and value are separated
177+
// by an ASCII equals sign '='. Since a variable name must not be
178+
// empty, allow variable names starting with an equals sign. Skip all
179+
// malformed lines.
180+
if input.is_empty() {
181+
return None;
158182
}
183+
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
184+
pos.map(|p| (
185+
OsStringExt::from_vec(input[..p].to_vec()),
186+
OsStringExt::from_vec(input[p+1..].to_vec()),
187+
))
159188
}
160-
Env { iter: variables.into_iter(), _dont_send_or_sync_me: PhantomData }
161189
}
162190

163-
pub fn getenv(key: &OsStr) -> io::Result<Option<OsString>> {
164-
if ! key.is_empty() {
165-
if let Ok(mut file) = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap())) {
166-
let mut string = String::new();
167-
file.read_to_string(&mut string)?;
168-
Ok(Some(OsString::from(string)))
191+
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
192+
// environment variables with a nul byte can't be set, so their value is
193+
// always None as well
194+
let k = CString::new(k.as_bytes())?;
195+
unsafe {
196+
let _guard = ENV_LOCK.lock();
197+
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
198+
let ret = if s.is_null() {
199+
None
169200
} else {
170-
Ok(None)
171-
}
172-
} else {
173-
Ok(None)
201+
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
202+
};
203+
Ok(ret)
174204
}
175205
}
176206

177-
pub fn setenv(key: &OsStr, value: &OsStr) -> io::Result<()> {
178-
if ! key.is_empty() {
179-
let mut file = ::fs::File::create(&("env:".to_owned() + key.to_str().unwrap()))?;
180-
file.write_all(value.as_bytes())?;
181-
file.set_len(value.len() as u64)?;
207+
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
208+
let k = CString::new(k.as_bytes())?;
209+
let v = CString::new(v.as_bytes())?;
210+
211+
unsafe {
212+
let _guard = ENV_LOCK.lock();
213+
cvt_libc(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ())
182214
}
183-
Ok(())
184215
}
185216

186-
pub fn unsetenv(key: &OsStr) -> io::Result<()> {
187-
::fs::remove_file(&("env:".to_owned() + key.to_str().unwrap()))?;
188-
Ok(())
217+
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
218+
let nbuf = CString::new(n.as_bytes())?;
219+
220+
unsafe {
221+
let _guard = ENV_LOCK.lock();
222+
cvt_libc(libc::unsetenv(nbuf.as_ptr())).map(|_| ())
223+
}
189224
}
190225

191226
pub fn page_size() -> usize {

src/libstd/sys/redox/process.rs

+96-19
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99
// except according to those terms.
1010

1111
use env::{split_paths};
12-
use ffi::OsStr;
13-
use os::unix::ffi::OsStrExt;
12+
use ffi::{CStr, OsStr};
1413
use fmt;
15-
use io::{self, Error, ErrorKind};
16-
use iter;
14+
use fs::File;
15+
use io::{self, prelude::*, BufReader, Error, ErrorKind, SeekFrom};
1716
use libc::{EXIT_SUCCESS, EXIT_FAILURE};
17+
use os::unix::ffi::OsStrExt;
1818
use path::{Path, PathBuf};
19+
use ptr;
20+
use sys::ext::fs::MetadataExt;
21+
use sys::ext::io::AsRawFd;
1922
use sys::fd::FileDesc;
20-
use sys::fs::{File, OpenOptions};
23+
use sys::fs::{File as SysFile, OpenOptions};
24+
use sys::os::{ENV_LOCK, environ};
2125
use sys::pipe::{self, AnonPipe};
2226
use sys::{cvt, syscall};
2327
use sys_common::process::{CommandEnv, DefaultEnvKey};
@@ -297,12 +301,6 @@ impl Command {
297301
t!(callback());
298302
}
299303

300-
let args: Vec<[usize; 2]> = iter::once(
301-
[self.program.as_ptr() as usize, self.program.len()]
302-
).chain(
303-
self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()])
304-
).collect();
305-
306304
self.env.apply();
307305

308306
let program = if self.program.contains(':') || self.program.contains('/') {
@@ -321,14 +319,93 @@ impl Command {
321319
None
322320
};
323321

324-
if let Some(program) = program {
325-
if let Err(err) = syscall::execve(program.as_os_str().as_bytes(), &args) {
326-
io::Error::from_raw_os_error(err.errno as i32)
322+
let mut file = if let Some(program) = program {
323+
t!(File::open(program.as_os_str()))
324+
} else {
325+
return io::Error::from_raw_os_error(syscall::ENOENT);
326+
};
327+
328+
// Push all the arguments
329+
let mut args: Vec<[usize; 2]> = Vec::with_capacity(1 + self.args.len());
330+
331+
let interpreter = {
332+
let mut reader = BufReader::new(&file);
333+
334+
let mut shebang = [0; 2];
335+
let mut read = 0;
336+
loop {
337+
match t!(reader.read(&mut shebang[read..])) {
338+
0 => break,
339+
n => read += n,
340+
}
341+
}
342+
343+
if &shebang == b"#!" {
344+
// This is an interpreted script.
345+
// First of all, since we'll be passing another file to
346+
// fexec(), we need to manually check that we have permission
347+
// to execute this file:
348+
let uid = t!(cvt(syscall::getuid()));
349+
let gid = t!(cvt(syscall::getgid()));
350+
let meta = t!(file.metadata());
351+
352+
let mode = if uid == meta.uid() as usize {
353+
meta.mode() >> 3*2 & 0o7
354+
} else if gid == meta.gid() as usize {
355+
meta.mode() >> 3*1 & 0o7
356+
} else {
357+
meta.mode() & 0o7
358+
};
359+
if mode & 1 == 0 {
360+
return io::Error::from_raw_os_error(syscall::EPERM);
361+
}
362+
363+
// Second of all, we need to actually read which interpreter it wants
364+
let mut interpreter = Vec::new();
365+
t!(reader.read_until(b'\n', &mut interpreter));
366+
// Pop one trailing newline, if any
367+
if interpreter.ends_with(&[b'\n']) {
368+
interpreter.pop().unwrap();
369+
}
370+
371+
// FIXME: Here we could just reassign `file` directly, if it
372+
// wasn't for lexical lifetimes. Remove the whole `let
373+
// interpreter = { ... };` hack once NLL lands.
374+
// NOTE: Although DO REMEMBER to make sure the interpreter path
375+
// still lives long enough to reach fexec.
376+
Some(interpreter)
327377
} else {
328-
panic!("return from exec without err");
378+
None
379+
}
380+
};
381+
if let Some(ref interpreter) = interpreter {
382+
let path: &OsStr = OsStr::from_bytes(&interpreter);
383+
file = t!(File::open(path));
384+
385+
args.push([interpreter.as_ptr() as usize, interpreter.len()]);
386+
} else {
387+
t!(file.seek(SeekFrom::Start(0)));
388+
}
389+
390+
args.push([self.program.as_ptr() as usize, self.program.len()]);
391+
args.extend(self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()]));
392+
393+
// Push all the variables
394+
let mut vars: Vec<[usize; 2]> = Vec::new();
395+
{
396+
let _guard = ENV_LOCK.lock();
397+
let mut environ = *environ();
398+
while *environ != ptr::null() {
399+
let var = CStr::from_ptr(*environ).to_bytes();
400+
vars.push([var.as_ptr() as usize, var.len()]);
401+
environ = environ.offset(1);
329402
}
403+
}
404+
405+
if let Err(err) = syscall::fexec(file.as_raw_fd(), &args, &vars) {
406+
io::Error::from_raw_os_error(err.errno as i32)
330407
} else {
331-
io::Error::from_raw_os_error(syscall::ENOENT)
408+
panic!("return from exec without err");
332409
}
333410
}
334411

@@ -392,7 +469,7 @@ impl Stdio {
392469
let mut opts = OpenOptions::new();
393470
opts.read(readable);
394471
opts.write(!readable);
395-
let fd = File::open(Path::new("null:"), &opts)?;
472+
let fd = SysFile::open(Path::new("null:"), &opts)?;
396473
Ok((ChildStdio::Owned(fd.into_fd()), None))
397474
}
398475
}
@@ -405,8 +482,8 @@ impl From<AnonPipe> for Stdio {
405482
}
406483
}
407484

408-
impl From<File> for Stdio {
409-
fn from(file: File) -> Stdio {
485+
impl From<SysFile> for Stdio {
486+
fn from(file: SysFile) -> Stdio {
410487
Stdio::Fd(file.into_fd())
411488
}
412489
}

src/libstd/sys/redox/syscall/call.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,6 @@ pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> {
8282
unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) }
8383
}
8484

85-
/// Replace the current process with a new executable
86-
pub fn execve<T: AsRef<[u8]>>(path: T, args: &[[usize; 2]]) -> Result<usize> {
87-
unsafe { syscall4(SYS_EXECVE, path.as_ref().as_ptr() as usize,
88-
path.as_ref().len(), args.as_ptr() as usize, args.len()) }
89-
}
90-
9185
/// Exit the current process
9286
pub fn exit(status: usize) -> Result<usize> {
9387
unsafe { syscall1(SYS_EXIT, status) }
@@ -110,9 +104,10 @@ pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> {
110104
unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) }
111105
}
112106

113-
/// Register a file for event-based I/O
114-
pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
115-
unsafe { syscall2(SYS_FEVENT, fd, flags) }
107+
/// Replace the current process with a new executable
108+
pub fn fexec(fd: usize, args: &[[usize; 2]], vars: &[[usize; 2]]) -> Result<usize> {
109+
unsafe { syscall5(SYS_FEXEC, fd, args.as_ptr() as usize, args.len(),
110+
vars.as_ptr() as usize, vars.len()) }
116111
}
117112

118113
/// Map a file into memory

0 commit comments

Comments
 (0)