about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-11-15 12:43:01 +0000
committerbors <bors@rust-lang.org>2018-11-15 12:43:01 +0000
commit9649c1f70fddd01843024932df97fb5a2b10bfe8 (patch)
treebc6af666244208eb4354636428ac26731935cbdc /src/libstd
parent99e3fca27d141e2d100ebaf5d5a4104234ae201a (diff)
parentd0e08ce88e0926e8e2bc393d3d7982fd767f37b8 (diff)
downloadrust-9649c1f70fddd01843024932df97fb5a2b10bfe8.tar.gz
rust-9649c1f70fddd01843024932df97fb5a2b10bfe8.zip
Auto merge of #55974 - pietroalbini:rollup, r=pietroalbini
Rollup of 17 pull requests

Successful merges:

 - #55182 (Redox: Update to new changes)
 - #55211 (Add BufWriter::buffer method)
 - #55507 (Add link to std::mem::size_of to size_of intrinsic documentation)
 - #55530 (Speed up String::from_utf16)
 - #55556 (Use `Mmap` to open the rmeta file.)
 - #55622 (NetBSD: link libstd with librt in addition to libpthread)
 - #55750 (Make `NodeId` and `HirLocalId` `newtype_index`)
 - #55778 (Wrap some query results in `Lrc`.)
 - #55781 (More precise spans for temps and their drops)
 - #55785 (Add mem::forget_unsized() for forgetting unsized values)
 - #55852 (Rewrite `...` as `..=` as a `MachineApplicable` 2018 idiom lint)
 - #55865 (Unix RwLock: avoid racy access to write_locked)
 - #55901 (fix various typos in doc comments)
 - #55926 (Change sidebar selector to fix compatibility with docs.rs)
 - #55930 (A handful of hir tweaks)
 - #55932 (core/char: Speed up `to_digit()` for `radix <= 10`)
 - #55956 (add tests for some fixed ICEs)

Failed merges:

r? @ghost
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/alloc.rs2
-rw-r--r--src/libstd/build.rs5
-rw-r--r--src/libstd/collections/hash/map.rs2
-rw-r--r--src/libstd/io/buffered.rs48
-rw-r--r--src/libstd/keyword_docs.rs2
-rw-r--r--src/libstd/net/ip.rs2
-rw-r--r--src/libstd/process.rs4
-rw-r--r--src/libstd/sys/redox/fd.rs2
-rw-r--r--src/libstd/sys/redox/mod.rs23
-rw-r--r--src/libstd/sys/redox/os.rs101
-rw-r--r--src/libstd/sys/redox/process.rs115
-rw-r--r--src/libstd/sys/redox/syscall/call.rs17
-rw-r--r--src/libstd/sys/redox/syscall/flag.rs44
-rw-r--r--src/libstd/sys/redox/syscall/number.rs2
-rw-r--r--src/libstd/sys/unix/fd.rs2
-rw-r--r--src/libstd/sys/unix/rwlock.rs6
-rw-r--r--src/libstd/thread/mod.rs2
17 files changed, 291 insertions, 88 deletions
diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs
index 9c057396470..b024574427e 100644
--- a/src/libstd/alloc.rs
+++ b/src/libstd/alloc.rs
@@ -85,7 +85,7 @@ pub use alloc_crate::alloc::*;
 /// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
 /// plus related functions.
 ///
-/// This type implements the `GlobalAlloc` trait and Rust programs by deafult
+/// This type implements the `GlobalAlloc` trait and Rust programs by default
 /// work as if they had this definition:
 ///
 /// ```rust
diff --git a/src/libstd/build.rs b/src/libstd/build.rs
index b3851d22841..9d6e8c4cafd 100644
--- a/src/libstd/build.rs
+++ b/src/libstd/build.rs
@@ -41,8 +41,11 @@ fn main() {
     } else if target.contains("freebsd") {
         println!("cargo:rustc-link-lib=execinfo");
         println!("cargo:rustc-link-lib=pthread");
+    } else if target.contains("netbsd") {
+        println!("cargo:rustc-link-lib=pthread");
+        println!("cargo:rustc-link-lib=rt");
     } else if target.contains("dragonfly") || target.contains("bitrig") ||
-              target.contains("netbsd") || target.contains("openbsd") {
+              target.contains("openbsd") {
         println!("cargo:rustc-link-lib=pthread");
     } else if target.contains("solaris") {
         println!("cargo:rustc-link-lib=socket");
diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs
index 8de415e8aed..bb2f152edc6 100644
--- a/src/libstd/collections/hash/map.rs
+++ b/src/libstd/collections/hash/map.rs
@@ -1569,7 +1569,7 @@ impl<K, V, S> HashMap<K, V, S>
     /// where the key should go, meaning the keys may become "lost" if their
     /// location does not reflect their state. For instance, if you change a key
     /// so that the map now contains keys which compare equal, search may start
-    /// acting eratically, with two keys randomly masking eachother. Implementations
+    /// acting erratically, with two keys randomly masking each other. Implementations
     /// are free to assume this doesn't happen (within the limits of memory-safety).
     #[unstable(feature = "hash_raw_entry", issue = "54043")]
     pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<K, V, S> {
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index e26e6d391f8..476ee3f71ca 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -525,6 +525,25 @@ impl<W: Write> BufWriter<W> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() }
 
+    /// Returns a reference to the internally buffered data.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # #![feature(bufreader_buffer)]
+    /// use std::io::BufWriter;
+    /// use std::net::TcpStream;
+    ///
+    /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
+    ///
+    /// // See how many bytes are currently buffered
+    /// let bytes_buffered = buf_writer.buffer().len();
+    /// ```
+    #[unstable(feature = "bufreader_buffer", issue = "45323")]
+    pub fn buffer(&self) -> &[u8] {
+        &self.buf
+    }
+
     /// Unwraps this `BufWriter`, returning the underlying writer.
     ///
     /// The buffer is written out before returning the writer.
@@ -965,31 +984,31 @@ mod tests {
         let mut buf = [0, 0, 0];
         let nread = reader.read(&mut buf);
         assert_eq!(nread.unwrap(), 3);
-        let b: &[_] = &[5, 6, 7];
-        assert_eq!(buf, b);
+        assert_eq!(buf, [5, 6, 7]);
+        assert_eq!(reader.buffer(), []);
 
         let mut buf = [0, 0];
         let nread = reader.read(&mut buf);
         assert_eq!(nread.unwrap(), 2);
-        let b: &[_] = &[0, 1];
-        assert_eq!(buf, b);
+        assert_eq!(buf, [0, 1]);
+        assert_eq!(reader.buffer(), []);
 
         let mut buf = [0];
         let nread = reader.read(&mut buf);
         assert_eq!(nread.unwrap(), 1);
-        let b: &[_] = &[2];
-        assert_eq!(buf, b);
+        assert_eq!(buf, [2]);
+        assert_eq!(reader.buffer(), [3]);
 
         let mut buf = [0, 0, 0];
         let nread = reader.read(&mut buf);
         assert_eq!(nread.unwrap(), 1);
-        let b: &[_] = &[3, 0, 0];
-        assert_eq!(buf, b);
+        assert_eq!(buf, [3, 0, 0]);
+        assert_eq!(reader.buffer(), []);
 
         let nread = reader.read(&mut buf);
         assert_eq!(nread.unwrap(), 1);
-        let b: &[_] = &[4, 0, 0];
-        assert_eq!(buf, b);
+        assert_eq!(buf, [4, 0, 0]);
+        assert_eq!(reader.buffer(), []);
 
         assert_eq!(reader.read(&mut buf).unwrap(), 0);
     }
@@ -1078,31 +1097,40 @@ mod tests {
         let mut writer = BufWriter::with_capacity(2, inner);
 
         writer.write(&[0, 1]).unwrap();
+        assert_eq!(writer.buffer(), []);
         assert_eq!(*writer.get_ref(), [0, 1]);
 
         writer.write(&[2]).unwrap();
+        assert_eq!(writer.buffer(), [2]);
         assert_eq!(*writer.get_ref(), [0, 1]);
 
         writer.write(&[3]).unwrap();
+        assert_eq!(writer.buffer(), [2, 3]);
         assert_eq!(*writer.get_ref(), [0, 1]);
 
         writer.flush().unwrap();
+        assert_eq!(writer.buffer(), []);
         assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
 
         writer.write(&[4]).unwrap();
         writer.write(&[5]).unwrap();
+        assert_eq!(writer.buffer(), [4, 5]);
         assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
 
         writer.write(&[6]).unwrap();
+        assert_eq!(writer.buffer(), [6]);
         assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]);
 
         writer.write(&[7, 8]).unwrap();
+        assert_eq!(writer.buffer(), []);
         assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]);
 
         writer.write(&[9, 10, 11]).unwrap();
+        assert_eq!(writer.buffer(), []);
         assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
 
         writer.flush().unwrap();
+        assert_eq!(writer.buffer(), []);
         assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
     }
 
diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs
index 6c95854c66c..13cf3133dcd 100644
--- a/src/libstd/keyword_docs.rs
+++ b/src/libstd/keyword_docs.rs
@@ -615,7 +615,7 @@ mod loop_keyword { }
 //
 /// The keyword used to define structs.
 ///
-/// Structs in Rust come in three flavours: Structs with named fields, tuple structs, and unit
+/// Structs in Rust come in three flavors: Structs with named fields, tuple structs, and unit
 /// structs.
 ///
 /// ```rust
diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs
index 2517c45696a..8685cb73887 100644
--- a/src/libstd/net/ip.rs
+++ b/src/libstd/net/ip.rs
@@ -852,7 +852,7 @@ impl From<[u8; 4]> for IpAddr {
 impl Ipv6Addr {
     /// Creates a new IPv6 address from eight 16-bit segments.
     ///
-    /// The result will represent the IP address a:b:c:d:e:f:g:h.
+    /// The result will represent the IP address `a:b:c:d:e:f:g:h`.
     ///
     /// # Examples
     ///
diff --git a/src/libstd/process.rs b/src/libstd/process.rs
index 327ad7f64c2..51481e129df 100644
--- a/src/libstd/process.rs
+++ b/src/libstd/process.rs
@@ -1122,7 +1122,7 @@ impl From<fs::File> for Stdio {
     /// let file = File::open("foo.txt").unwrap();
     ///
     /// let reverse = Command::new("rev")
-    ///     .stdin(file)  // Implicit File convertion into a Stdio
+    ///     .stdin(file)  // Implicit File conversion into a Stdio
     ///     .output()
     ///     .expect("failed reverse command");
     ///
@@ -1340,7 +1340,7 @@ impl Child {
     /// Attempts to collect the exit status of the child if it has already
     /// exited.
     ///
-    /// This function will not block the calling thread and will only advisorily
+    /// This function will not block the calling thread and will only
     /// check to see if the child process has exited or not. If the child has
     /// exited then on Unix the process id is reaped. This function is
     /// guaranteed to repeatedly return a successful exit status so long as the
diff --git a/src/libstd/sys/redox/fd.rs b/src/libstd/sys/redox/fd.rs
index d61103a872f..d62b09134bb 100644
--- a/src/libstd/sys/redox/fd.rs
+++ b/src/libstd/sys/redox/fd.rs
@@ -26,7 +26,7 @@ impl FileDesc {
 
     pub fn raw(&self) -> usize { self.fd }
 
-    /// Extracts the actual filedescriptor without closing it.
+    /// Extracts the actual file descriptor without closing it.
     pub fn into_raw(self) -> usize {
         let fd = self.fd;
         mem::forget(self);
diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs
index edb407ecd23..3543d8f8fa3 100644
--- a/src/libstd/sys/redox/mod.rs
+++ b/src/libstd/sys/redox/mod.rs
@@ -77,6 +77,29 @@ pub fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
     result.map_err(|err| io::Error::from_raw_os_error(err.errno))
 }
 
+#[doc(hidden)]
+pub trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+    ($($t:ident)*) => ($(impl IsMinusOne for $t {
+        fn is_minus_one(&self) -> bool {
+            *self == -1
+        }
+    })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+pub fn cvt_libc<T: IsMinusOne>(t: T) -> io::Result<T> {
+    if t.is_minus_one() {
+        Err(io::Error::last_os_error())
+    } else {
+        Ok(t)
+    }
+}
+
 /// On Redox, use an illegal instruction to abort
 pub unsafe fn abort_internal() -> ! {
     ::core::intrinsics::abort();
diff --git a/src/libstd/sys/redox/os.rs b/src/libstd/sys/redox/os.rs
index 5822216779b..84eb56615b6 100644
--- a/src/libstd/sys/redox/os.rs
+++ b/src/libstd/sys/redox/os.rs
@@ -12,10 +12,12 @@
 
 #![allow(unused_imports)] // lots of cfg code here
 
+use libc::{self, c_char};
+
 use os::unix::prelude::*;
 
 use error::Error as StdError;
-use ffi::{OsString, OsStr};
+use ffi::{CStr, CString, OsStr, OsString};
 use fmt;
 use io::{self, Read, Write};
 use iter;
@@ -27,7 +29,7 @@ use ptr;
 use slice;
 use str;
 use sys_common::mutex::Mutex;
-use sys::{cvt, fd, syscall};
+use sys::{cvt, cvt_libc, fd, syscall};
 use vec;
 
 extern {
@@ -129,6 +131,8 @@ pub fn current_exe() -> io::Result<PathBuf> {
     Ok(PathBuf::from(path))
 }
 
+pub static ENV_LOCK: Mutex = Mutex::new();
+
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
     _dont_send_or_sync_me: PhantomData<*mut ()>,
@@ -140,52 +144,83 @@ impl Iterator for Env {
     fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
 }
 
+pub unsafe fn environ() -> *mut *const *const c_char {
+    extern { static mut environ: *const *const c_char; }
+    &mut environ
+}
+
 /// Returns a vector of (variable, value) byte-vector pairs for all the
 /// environment variables of the current process.
 pub fn env() -> Env {
-    let mut variables: Vec<(OsString, OsString)> = Vec::new();
-    if let Ok(mut file) = ::fs::File::open("env:") {
-        let mut string = String::new();
-        if file.read_to_string(&mut string).is_ok() {
-            for line in string.lines() {
-                let mut parts = line.splitn(2, '=');
-                if let Some(name) = parts.next() {
-                    let value = parts.next().unwrap_or("");
-                    variables.push((OsString::from(name.to_string()),
-                                    OsString::from(value.to_string())));
-                }
+    unsafe {
+        let _guard = ENV_LOCK.lock();
+        let mut environ = *environ();
+        if environ == ptr::null() {
+            panic!("os::env() failure getting env string from OS: {}",
+                   io::Error::last_os_error());
+        }
+        let mut result = Vec::new();
+        while *environ != ptr::null() {
+            if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+                result.push(key_value);
             }
+            environ = environ.offset(1);
+        }
+        return Env {
+            iter: result.into_iter(),
+            _dont_send_or_sync_me: PhantomData,
+        }
+    }
+
+    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+        // Strategy (copied from glibc): Variable name and value are separated
+        // by an ASCII equals sign '='. Since a variable name must not be
+        // empty, allow variable names starting with an equals sign. Skip all
+        // malformed lines.
+        if input.is_empty() {
+            return None;
         }
+        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+        pos.map(|p| (
+            OsStringExt::from_vec(input[..p].to_vec()),
+            OsStringExt::from_vec(input[p+1..].to_vec()),
+        ))
     }
-    Env { iter: variables.into_iter(), _dont_send_or_sync_me: PhantomData }
 }
 
-pub fn getenv(key: &OsStr) -> io::Result<Option<OsString>> {
-    if ! key.is_empty() {
-        if let Ok(mut file) = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap())) {
-            let mut string = String::new();
-            file.read_to_string(&mut string)?;
-            Ok(Some(OsString::from(string)))
+pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+    // environment variables with a nul byte can't be set, so their value is
+    // always None as well
+    let k = CString::new(k.as_bytes())?;
+    unsafe {
+        let _guard = ENV_LOCK.lock();
+        let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+        let ret = if s.is_null() {
+            None
         } else {
-            Ok(None)
-        }
-    } else {
-        Ok(None)
+            Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+        };
+        Ok(ret)
     }
 }
 
-pub fn setenv(key: &OsStr, value: &OsStr) -> io::Result<()> {
-    if ! key.is_empty() {
-        let mut file = ::fs::File::create(&("env:".to_owned() + key.to_str().unwrap()))?;
-        file.write_all(value.as_bytes())?;
-        file.set_len(value.len() as u64)?;
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let k = CString::new(k.as_bytes())?;
+    let v = CString::new(v.as_bytes())?;
+
+    unsafe {
+        let _guard = ENV_LOCK.lock();
+        cvt_libc(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ())
     }
-    Ok(())
 }
 
-pub fn unsetenv(key: &OsStr) -> io::Result<()> {
-    ::fs::remove_file(&("env:".to_owned() + key.to_str().unwrap()))?;
-    Ok(())
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+    let nbuf = CString::new(n.as_bytes())?;
+
+    unsafe {
+        let _guard = ENV_LOCK.lock();
+        cvt_libc(libc::unsetenv(nbuf.as_ptr())).map(|_| ())
+    }
 }
 
 pub fn page_size() -> usize {
diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs
index 2037616e6ac..4370c1e0502 100644
--- a/src/libstd/sys/redox/process.rs
+++ b/src/libstd/sys/redox/process.rs
@@ -9,15 +9,19 @@
 // except according to those terms.
 
 use env::{split_paths};
-use ffi::OsStr;
-use os::unix::ffi::OsStrExt;
+use ffi::{CStr, OsStr};
 use fmt;
-use io::{self, Error, ErrorKind};
-use iter;
+use fs::File;
+use io::{self, prelude::*, BufReader, Error, ErrorKind, SeekFrom};
 use libc::{EXIT_SUCCESS, EXIT_FAILURE};
+use os::unix::ffi::OsStrExt;
 use path::{Path, PathBuf};
+use ptr;
+use sys::ext::fs::MetadataExt;
+use sys::ext::io::AsRawFd;
 use sys::fd::FileDesc;
-use sys::fs::{File, OpenOptions};
+use sys::fs::{File as SysFile, OpenOptions};
+use sys::os::{ENV_LOCK, environ};
 use sys::pipe::{self, AnonPipe};
 use sys::{cvt, syscall};
 use sys_common::process::{CommandEnv, DefaultEnvKey};
@@ -297,12 +301,6 @@ impl Command {
             t!(callback());
         }
 
-        let args: Vec<[usize; 2]> = iter::once(
-            [self.program.as_ptr() as usize, self.program.len()]
-        ).chain(
-            self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()])
-        ).collect();
-
         self.env.apply();
 
         let program = if self.program.contains(':') || self.program.contains('/') {
@@ -321,14 +319,93 @@ impl Command {
             None
         };
 
-        if let Some(program) = program {
-            if let Err(err) = syscall::execve(program.as_os_str().as_bytes(), &args) {
-                io::Error::from_raw_os_error(err.errno as i32)
+        let mut file = if let Some(program) = program {
+            t!(File::open(program.as_os_str()))
+        } else {
+            return io::Error::from_raw_os_error(syscall::ENOENT);
+        };
+
+        // Push all the arguments
+        let mut args: Vec<[usize; 2]> = Vec::with_capacity(1 + self.args.len());
+
+        let interpreter = {
+            let mut reader = BufReader::new(&file);
+
+            let mut shebang = [0; 2];
+            let mut read = 0;
+            loop {
+                match t!(reader.read(&mut shebang[read..])) {
+                    0 => break,
+                    n => read += n,
+                }
+            }
+
+            if &shebang == b"#!" {
+                // This is an interpreted script.
+                // First of all, since we'll be passing another file to
+                // fexec(), we need to manually check that we have permission
+                // to execute this file:
+                let uid = t!(cvt(syscall::getuid()));
+                let gid = t!(cvt(syscall::getgid()));
+                let meta = t!(file.metadata());
+
+                let mode = if uid == meta.uid() as usize {
+                    meta.mode() >> 3*2 & 0o7
+                } else if gid == meta.gid() as usize {
+                    meta.mode() >> 3*1 & 0o7
+                } else {
+                    meta.mode() & 0o7
+                };
+                if mode & 1 == 0 {
+                    return io::Error::from_raw_os_error(syscall::EPERM);
+                }
+
+                // Second of all, we need to actually read which interpreter it wants
+                let mut interpreter = Vec::new();
+                t!(reader.read_until(b'\n', &mut interpreter));
+                // Pop one trailing newline, if any
+                if interpreter.ends_with(&[b'\n']) {
+                    interpreter.pop().unwrap();
+                }
+
+                // FIXME: Here we could just reassign `file` directly, if it
+                // wasn't for lexical lifetimes. Remove the whole `let
+                // interpreter = { ... };` hack once NLL lands.
+                // NOTE: Although DO REMEMBER to make sure the interpreter path
+                // still lives long enough to reach fexec.
+                Some(interpreter)
             } else {
-                panic!("return from exec without err");
+                None
+            }
+        };
+        if let Some(ref interpreter) = interpreter {
+            let path: &OsStr = OsStr::from_bytes(&interpreter);
+            file = t!(File::open(path));
+
+            args.push([interpreter.as_ptr() as usize, interpreter.len()]);
+        } else {
+            t!(file.seek(SeekFrom::Start(0)));
+        }
+
+        args.push([self.program.as_ptr() as usize, self.program.len()]);
+        args.extend(self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()]));
+
+        // Push all the variables
+        let mut vars: Vec<[usize; 2]> = Vec::new();
+        {
+            let _guard = ENV_LOCK.lock();
+            let mut environ = *environ();
+            while *environ != ptr::null() {
+                let var = CStr::from_ptr(*environ).to_bytes();
+                vars.push([var.as_ptr() as usize, var.len()]);
+                environ = environ.offset(1);
             }
+        }
+
+        if let Err(err) = syscall::fexec(file.as_raw_fd(), &args, &vars) {
+            io::Error::from_raw_os_error(err.errno as i32)
         } else {
-            io::Error::from_raw_os_error(syscall::ENOENT)
+            panic!("return from exec without err");
         }
     }
 
@@ -392,7 +469,7 @@ impl Stdio {
                 let mut opts = OpenOptions::new();
                 opts.read(readable);
                 opts.write(!readable);
-                let fd = File::open(Path::new("null:"), &opts)?;
+                let fd = SysFile::open(Path::new("null:"), &opts)?;
                 Ok((ChildStdio::Owned(fd.into_fd()), None))
             }
         }
@@ -405,8 +482,8 @@ impl From<AnonPipe> for Stdio {
     }
 }
 
-impl From<File> for Stdio {
-    fn from(file: File) -> Stdio {
+impl From<SysFile> for Stdio {
+    fn from(file: SysFile) -> Stdio {
         Stdio::Fd(file.into_fd())
     }
 }
diff --git a/src/libstd/sys/redox/syscall/call.rs b/src/libstd/sys/redox/syscall/call.rs
index f9a8bd61ac8..577dde4b4be 100644
--- a/src/libstd/sys/redox/syscall/call.rs
+++ b/src/libstd/sys/redox/syscall/call.rs
@@ -45,7 +45,7 @@ pub unsafe fn brk(addr: usize) -> Result<usize> {
 /// # Errors
 ///
 /// * `EACCES` - permission is denied for one of the components of `path`, or `path`
-/// * `EFAULT` - `path` does not point to the process's addressible memory
+/// * `EFAULT` - `path` does not point to the process's addressable memory
 /// * `EIO` - an I/O error occurred
 /// * `ENOENT` - `path` does not exit
 /// * `ENOTDIR` - `path` is not a directory
@@ -82,12 +82,6 @@ pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> {
     unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) }
 }
 
-/// Replace the current process with a new executable
-pub fn execve<T: AsRef<[u8]>>(path: T, args: &[[usize; 2]]) -> Result<usize> {
-    unsafe { syscall4(SYS_EXECVE, path.as_ref().as_ptr() as usize,
-                      path.as_ref().len(), args.as_ptr() as usize, args.len()) }
-}
-
 /// Exit the current process
 pub fn exit(status: usize) -> Result<usize> {
     unsafe { syscall1(SYS_EXIT, status) }
@@ -110,9 +104,10 @@ pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> {
     unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) }
 }
 
-/// Register a file for event-based I/O
-pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
-    unsafe { syscall2(SYS_FEVENT, fd, flags) }
+/// Replace the current process with a new executable
+pub fn fexec(fd: usize, args: &[[usize; 2]], vars: &[[usize; 2]]) -> Result<usize> {
+    unsafe { syscall5(SYS_FEXEC, fd, args.as_ptr() as usize, args.len(),
+                      vars.as_ptr() as usize, vars.len()) }
 }
 
 /// Map a file into memory
@@ -347,7 +342,7 @@ pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result<usize>
 ///
 /// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block
 /// * `EBADF` - the file descriptor is not valid or is not open for writing
-/// * `EFAULT` - `buf` does not point to the process's addressible memory
+/// * `EFAULT` - `buf` does not point to the process's addressable memory
 /// * `EIO` - an I/O error occurred
 /// * `ENOSPC` - the device containing the file descriptor has no room for data
 /// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed
diff --git a/src/libstd/sys/redox/syscall/flag.rs b/src/libstd/sys/redox/syscall/flag.rs
index 0f61b9fa77b..0f1a2c23159 100644
--- a/src/libstd/sys/redox/syscall/flag.rs
+++ b/src/libstd/sys/redox/syscall/flag.rs
@@ -113,4 +113,46 @@ pub const SA_RESTART: usize =   0x10000000;
 pub const SA_NODEFER: usize =   0x40000000;
 pub const SA_RESETHAND: usize = 0x80000000;
 
-pub const WNOHANG: usize = 1;
+pub const WNOHANG: usize =    0x01;
+pub const WUNTRACED: usize =  0x02;
+pub const WCONTINUED: usize = 0x08;
+
+/// True if status indicates the child is stopped.
+pub fn wifstopped(status: usize) -> bool {
+    (status & 0xff) == 0x7f
+}
+
+/// If wifstopped(status), the signal that stopped the child.
+pub fn wstopsig(status: usize) -> usize {
+    (status >> 8) & 0xff
+}
+
+/// True if status indicates the child continued after a stop.
+pub fn wifcontinued(status: usize) -> bool {
+    status == 0xffff
+}
+
+/// True if STATUS indicates termination by a signal.
+pub fn wifsignaled(status: usize) -> bool {
+    ((status & 0x7f) + 1) as i8 >= 2
+}
+
+/// If wifsignaled(status), the terminating signal.
+pub fn wtermsig(status: usize) -> usize {
+    status & 0x7f
+}
+
+/// True if status indicates normal termination.
+pub fn wifexited(status: usize) -> bool {
+    wtermsig(status) == 0
+}
+
+/// If wifexited(status), the exit status.
+pub fn wexitstatus(status: usize) -> usize {
+    (status >> 8) & 0xff
+}
+
+/// True if status indicates a core dump was created.
+pub fn wcoredump(status: usize) -> bool {
+    (status & 0x80) != 0
+}
diff --git a/src/libstd/sys/redox/syscall/number.rs b/src/libstd/sys/redox/syscall/number.rs
index 45cb40e3908..1e187565a67 100644
--- a/src/libstd/sys/redox/syscall/number.rs
+++ b/src/libstd/sys/redox/syscall/number.rs
@@ -36,6 +36,7 @@ pub const SYS_FCHMOD: usize =   SYS_CLASS_FILE | 94;
 pub const SYS_FCHOWN: usize =   SYS_CLASS_FILE | 207;
 pub const SYS_FCNTL: usize =    SYS_CLASS_FILE | 55;
 pub const SYS_FEVENT: usize =   SYS_CLASS_FILE | 927;
+pub const SYS_FEXEC: usize =    SYS_CLASS_FILE | 11;
 pub const SYS_FMAP: usize =     SYS_CLASS_FILE | 90;
 pub const SYS_FUNMAP: usize =   SYS_CLASS_FILE | 91;
 pub const SYS_FPATH: usize =    SYS_CLASS_FILE | SYS_ARG_MSLICE | 928;
@@ -50,7 +51,6 @@ pub const SYS_BRK: usize =      45;
 pub const SYS_CHDIR: usize =    12;
 pub const SYS_CLOCK_GETTIME: usize = 265;
 pub const SYS_CLONE: usize =    120;
-pub const SYS_EXECVE: usize =   11;
 pub const SYS_EXIT: usize =     1;
 pub const SYS_FUTEX: usize =    240;
 pub const SYS_GETCWD: usize =   183;
diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs
index af33d2636fb..5a81d6dfb67 100644
--- a/src/libstd/sys/unix/fd.rs
+++ b/src/libstd/sys/unix/fd.rs
@@ -46,7 +46,7 @@ impl FileDesc {
 
     pub fn raw(&self) -> c_int { self.fd }
 
-    /// Extracts the actual filedescriptor without closing it.
+    /// Extracts the actual file descriptor without closing it.
     pub fn into_raw(self) -> c_int {
         let fd = self.fd;
         mem::forget(self);
diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs
index c754d5b8359..fcd6f7a27b6 100644
--- a/src/libstd/sys/unix/rwlock.rs
+++ b/src/libstd/sys/unix/rwlock.rs
@@ -14,7 +14,7 @@ use sync::atomic::{AtomicUsize, Ordering};
 
 pub struct RWLock {
     inner: UnsafeCell<libc::pthread_rwlock_t>,
-    write_locked: UnsafeCell<bool>,
+    write_locked: UnsafeCell<bool>, // guarded by the `inner` RwLock
     num_readers: AtomicUsize,
 }
 
@@ -52,13 +52,13 @@ impl RWLock {
         // allow that because it could lead to aliasing issues.
         if r == libc::EAGAIN {
             panic!("rwlock maximum reader count exceeded");
-        } else if r == libc::EDEADLK || *self.write_locked.get() {
+        } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) {
             if r == 0 {
                 self.raw_unlock();
             }
             panic!("rwlock read lock would result in deadlock");
         } else {
-            debug_assert_eq!(r, 0);
+            assert_eq!(r, 0);
             self.num_readers.fetch_add(1, Ordering::Relaxed);
         }
     }
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index a57b8dc7237..8a845efd413 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -233,7 +233,7 @@ pub use self::local::{LocalKey, AccessError};
 ///
 /// You may want to use [`spawn`] instead of [`thread::spawn`], when you want
 /// to recover from a failure to launch a thread, indeed the free function will
-/// panick where the `Builder` method will return a [`io::Result`].
+/// panic where the `Builder` method will return a [`io::Result`].
 ///
 /// # Examples
 ///