summary refs log tree commit diff
path: root/src/libstd/io
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2013-11-10 22:46:32 -0800
committerAlex Crichton <alex@alexcrichton.com>2013-11-11 20:44:07 -0800
commit49ee49296b65f3d807142f3326bee71dd7e13290 (patch)
treeb3380df09c8a10473820969a62f5775832255fda /src/libstd/io
parent8b4683d79d4b74f53808470cd2f98b23a0af9b93 (diff)
downloadrust-49ee49296b65f3d807142f3326bee71dd7e13290.tar.gz
rust-49ee49296b65f3d807142f3326bee71dd7e13290.zip
Move std::rt::io to std::io
Diffstat (limited to 'src/libstd/io')
-rw-r--r--src/libstd/io/buffered.rs473
-rw-r--r--src/libstd/io/comm_adapters.rs57
-rw-r--r--src/libstd/io/extensions.rs491
-rw-r--r--src/libstd/io/flate.rs123
-rw-r--r--src/libstd/io/fs.rs1289
-rw-r--r--src/libstd/io/mem.rs308
-rw-r--r--src/libstd/io/mod.rs1226
-rw-r--r--src/libstd/io/native/file.rs761
-rw-r--r--src/libstd/io/native/process.rs734
-rw-r--r--src/libstd/io/native/stdio.rs63
-rw-r--r--src/libstd/io/net/addrinfo.rs128
-rw-r--r--src/libstd/io/net/ip.rs449
-rw-r--r--src/libstd/io/net/mod.rs18
-rw-r--r--src/libstd/io/net/tcp.rs725
-rw-r--r--src/libstd/io/net/udp.rs321
-rw-r--r--src/libstd/io/net/unix.rs295
-rw-r--r--src/libstd/io/option.rs181
-rw-r--r--src/libstd/io/pipe.rs89
-rw-r--r--src/libstd/io/process.rs177
-rw-r--r--src/libstd/io/signal.rs224
-rw-r--r--src/libstd/io/stdio.rs321
-rw-r--r--src/libstd/io/timer.rs186
22 files changed, 8639 insertions, 0 deletions
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
new file mode 100644
index 00000000000..4932f392090
--- /dev/null
+++ b/src/libstd/io/buffered.rs
@@ -0,0 +1,473 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Buffering wrappers for I/O traits
+//!
+//! It can be excessively inefficient to work directly with a `Reader` or
+//! `Writer`. Every call to `read` or `write` on `TcpStream` results in a
+//! system call, for example. This module provides structures that wrap
+//! `Readers`, `Writers`, and `Streams` and buffer input and output to them.
+//!
+//! # Examples
+//!
+//! ```
+//! let tcp_stream = TcpStream::connect(addr);
+//! let reader = BufferedReader::new(tcp_stream);
+//!
+//! let mut buf: ~[u8] = vec::from_elem(100, 0u8);
+//! match reader.read(buf.as_slice()) {
+//!     Some(nread) => println!("Read {} bytes", nread),
+//!     None => println!("At the end of the stream!")
+//! }
+//! ```
+//!
+//! ```
+//! let tcp_stream = TcpStream::connect(addr);
+//! let writer = BufferedWriter::new(tcp_stream);
+//!
+//! writer.write("hello, world".as_bytes());
+//! writer.flush();
+//! ```
+//!
+//! ```
+//! let tcp_stream = TcpStream::connect(addr);
+//! let stream = BufferedStream::new(tcp_stream);
+//!
+//! stream.write("hello, world".as_bytes());
+//! stream.flush();
+//!
+//! let mut buf = vec::from_elem(100, 0u8);
+//! match stream.read(buf.as_slice()) {
+//!     Some(nread) => println!("Read {} bytes", nread),
+//!     None => println!("At the end of the stream!")
+//! }
+//! ```
+//!
+
+use prelude::*;
+
+use num;
+use vec;
+use str;
+use super::{Reader, Writer, Stream, Decorator};
+
+// libuv recommends 64k buffers to maximize throughput
+// https://groups.google.com/forum/#!topic/libuv/oQO1HJAIDdA
+static DEFAULT_CAPACITY: uint = 64 * 1024;
+
+/// Wraps a Reader and buffers input from it
+pub struct BufferedReader<R> {
+    priv inner: R,
+    priv buf: ~[u8],
+    priv pos: uint,
+    priv cap: uint
+}
+
+impl<R: Reader> BufferedReader<R> {
+    /// Creates a new `BufferedReader` with with the specified buffer capacity
+    pub fn with_capacity(cap: uint, inner: R) -> BufferedReader<R> {
+        // It's *much* faster to create an uninitialized buffer than it is to
+        // fill everything in with 0. This buffer is entirely an implementation
+        // detail and is never exposed, so we're safe to not initialize
+        // everything up-front. This allows creation of BufferedReader instances
+        // to be very cheap (large mallocs are not nearly as expensive as large
+        // callocs).
+        let mut buf = vec::with_capacity(cap);
+        unsafe { vec::raw::set_len(&mut buf, cap); }
+        BufferedReader {
+            inner: inner,
+            buf: buf,
+            pos: 0,
+            cap: 0
+        }
+    }
+
+    /// Creates a new `BufferedReader` with a default buffer capacity
+    pub fn new(inner: R) -> BufferedReader<R> {
+        BufferedReader::with_capacity(DEFAULT_CAPACITY, inner)
+    }
+
+    /// Reads the next line of input, interpreted as a sequence of utf-8
+    /// encoded unicode codepoints. If a newline is encountered, then the
+    /// newline is contained in the returned string.
+    pub fn read_line(&mut self) -> Option<~str> {
+        self.read_until('\n' as u8).map(str::from_utf8_owned)
+    }
+
+    /// Reads a sequence of bytes leading up to a specified delimeter. Once the
+    /// specified byte is encountered, reading ceases and the bytes up to and
+    /// including the delimiter are returned.
+    pub fn read_until(&mut self, byte: u8) -> Option<~[u8]> {
+        let mut res = ~[];
+        let mut used;
+        loop {
+            {
+                let available = self.fill_buffer();
+                match available.iter().position(|&b| b == byte) {
+                    Some(i) => {
+                        res.push_all(available.slice_to(i + 1));
+                        used = i + 1;
+                        break
+                    }
+                    None => {
+                        res.push_all(available);
+                        used = available.len();
+                    }
+                }
+            }
+            if used == 0 {
+                break
+            }
+            self.pos += used;
+        }
+        self.pos += used;
+        return if res.len() == 0 {None} else {Some(res)};
+    }
+
+    fn fill_buffer<'a>(&'a mut self) -> &'a [u8] {
+        if self.pos == self.cap {
+            match self.inner.read(self.buf) {
+                Some(cap) => {
+                    self.pos = 0;
+                    self.cap = cap;
+                }
+                None => {}
+            }
+        }
+        return self.buf.slice(self.pos, self.cap);
+    }
+}
+
+impl<R: Reader> Reader for BufferedReader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        let nread = {
+            let available = self.fill_buffer();
+            if available.len() == 0 {
+                return None;
+            }
+            let nread = num::min(available.len(), buf.len());
+            vec::bytes::copy_memory(buf, available, nread);
+            nread
+        };
+        self.pos += nread;
+        Some(nread)
+    }
+
+    fn eof(&mut self) -> bool {
+        self.pos == self.cap && self.inner.eof()
+    }
+}
+
+impl<R: Reader> Decorator<R> for BufferedReader<R> {
+    fn inner(self) -> R {
+        self.inner
+    }
+
+    fn inner_ref<'a>(&'a self) -> &'a R {
+        &self.inner
+    }
+
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R {
+        &mut self.inner
+    }
+}
+
+/// Wraps a Writer and buffers output to it
+///
+/// Note that `BufferedWriter` will NOT flush its buffer when dropped.
+pub struct BufferedWriter<W> {
+    priv inner: W,
+    priv buf: ~[u8],
+    priv pos: uint
+}
+
+impl<W: Writer> BufferedWriter<W> {
+    /// Creates a new `BufferedWriter` with with the specified buffer capacity
+    pub fn with_capacity(cap: uint, inner: W) -> BufferedWriter<W> {
+        // See comments in BufferedReader for why this uses unsafe code.
+        let mut buf = vec::with_capacity(cap);
+        unsafe { vec::raw::set_len(&mut buf, cap); }
+        BufferedWriter {
+            inner: inner,
+            buf: buf,
+            pos: 0
+        }
+    }
+
+    /// Creates a new `BufferedWriter` with a default buffer capacity
+    pub fn new(inner: W) -> BufferedWriter<W> {
+        BufferedWriter::with_capacity(DEFAULT_CAPACITY, inner)
+    }
+}
+
+impl<W: Writer> Writer for BufferedWriter<W> {
+    fn write(&mut self, buf: &[u8]) {
+        if self.pos + buf.len() > self.buf.len() {
+            self.flush();
+        }
+
+        if buf.len() > self.buf.len() {
+            self.inner.write(buf);
+        } else {
+            let dst = self.buf.mut_slice_from(self.pos);
+            vec::bytes::copy_memory(dst, buf, buf.len());
+            self.pos += buf.len();
+        }
+    }
+
+    fn flush(&mut self) {
+        if self.pos != 0 {
+            self.inner.write(self.buf.slice_to(self.pos));
+            self.pos = 0;
+        }
+        self.inner.flush();
+    }
+}
+
+impl<W: Writer> Decorator<W> for BufferedWriter<W> {
+    fn inner(self) -> W { self.inner }
+    fn inner_ref<'a>(&'a self) -> &'a W { &self.inner }
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { &mut self.inner }
+}
+
+/// Wraps a Writer and buffers output to it, flushing whenever a newline (0xa,
+/// '\n') is detected.
+///
+/// Note that this structure does NOT flush the output when dropped.
+pub struct LineBufferedWriter<W> {
+    priv inner: BufferedWriter<W>,
+}
+
+impl<W: Writer> LineBufferedWriter<W> {
+    /// Creates a new `LineBufferedWriter`
+    pub fn new(inner: W) -> LineBufferedWriter<W> {
+        // Lines typically aren't that long, don't use a giant buffer
+        LineBufferedWriter {
+            inner: BufferedWriter::with_capacity(1024, inner)
+        }
+    }
+}
+
+impl<W: Writer> Writer for LineBufferedWriter<W> {
+    fn write(&mut self, buf: &[u8]) {
+        match buf.iter().position(|&b| b == '\n' as u8) {
+            Some(i) => {
+                self.inner.write(buf.slice_to(i + 1));
+                self.inner.flush();
+                self.inner.write(buf.slice_from(i + 1));
+            }
+            None => self.inner.write(buf),
+        }
+    }
+
+    fn flush(&mut self) { self.inner.flush() }
+}
+
+impl<W: Writer> Decorator<W> for LineBufferedWriter<W> {
+    fn inner(self) -> W { self.inner.inner() }
+    fn inner_ref<'a>(&'a self) -> &'a W { self.inner.inner_ref() }
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { self.inner.inner_mut_ref() }
+}
+
+struct InternalBufferedWriter<W>(BufferedWriter<W>);
+
+impl<W: Reader> Reader for InternalBufferedWriter<W> {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        self.inner.read(buf)
+    }
+
+    fn eof(&mut self) -> bool {
+        self.inner.eof()
+    }
+}
+
+/// Wraps a Stream and buffers input and output to and from it
+///
+/// Note that `BufferedStream` will NOT flush its output buffer when dropped.
+pub struct BufferedStream<S> {
+    priv inner: BufferedReader<InternalBufferedWriter<S>>
+}
+
+impl<S: Stream> BufferedStream<S> {
+    pub fn with_capacities(reader_cap: uint, writer_cap: uint, inner: S)
+                           -> BufferedStream<S> {
+        let writer = BufferedWriter::with_capacity(writer_cap, inner);
+        let internal_writer = InternalBufferedWriter(writer);
+        let reader = BufferedReader::with_capacity(reader_cap,
+                                                   internal_writer);
+        BufferedStream { inner: reader }
+    }
+
+    pub fn new(inner: S) -> BufferedStream<S> {
+        BufferedStream::with_capacities(DEFAULT_CAPACITY, DEFAULT_CAPACITY,
+                                        inner)
+    }
+}
+
+impl<S: Stream> Reader for BufferedStream<S> {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        self.inner.read(buf)
+    }
+
+    fn eof(&mut self) -> bool {
+        self.inner.eof()
+    }
+}
+
+impl<S: Stream> Writer for BufferedStream<S> {
+    fn write(&mut self, buf: &[u8]) {
+        self.inner.inner.write(buf)
+    }
+
+    fn flush(&mut self) {
+        self.inner.inner.flush()
+    }
+}
+
+impl<S: Stream> Decorator<S> for BufferedStream<S> {
+    fn inner(self) -> S {
+        self.inner.inner.inner()
+    }
+
+    fn inner_ref<'a>(&'a self) -> &'a S {
+        self.inner.inner.inner_ref()
+    }
+
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut S {
+        self.inner.inner.inner_mut_ref()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::*;
+    use super::*;
+    use io;
+    use super::super::mem::{MemReader, MemWriter};
+
+    #[test]
+    fn test_buffered_reader() {
+        let inner = MemReader::new(~[0, 1, 2, 3, 4]);
+        let mut reader = BufferedReader::with_capacity(2, inner);
+
+        let mut buf = [0, 0, 0];
+        let nread = reader.read(buf);
+        assert_eq!(Some(2), nread);
+        assert_eq!([0, 1, 0], buf);
+        assert!(!reader.eof());
+
+        let mut buf = [0];
+        let nread = reader.read(buf);
+        assert_eq!(Some(1), nread);
+        assert_eq!([2], buf);
+        assert!(!reader.eof());
+
+        let mut buf = [0, 0, 0];
+        let nread = reader.read(buf);
+        assert_eq!(Some(1), nread);
+        assert_eq!([3, 0, 0], buf);
+        assert!(!reader.eof());
+
+        let nread = reader.read(buf);
+        assert_eq!(Some(1), nread);
+        assert_eq!([4, 0, 0], buf);
+        assert!(reader.eof());
+
+        assert_eq!(None, reader.read(buf));
+    }
+
+    #[test]
+    fn test_buffered_writer() {
+        let inner = MemWriter::new();
+        let mut writer = BufferedWriter::with_capacity(2, inner);
+
+        writer.write([0, 1]);
+        assert_eq!([], writer.inner_ref().inner_ref().as_slice());
+
+        writer.write([2]);
+        assert_eq!([0, 1], writer.inner_ref().inner_ref().as_slice());
+
+        writer.write([3]);
+        assert_eq!([0, 1], writer.inner_ref().inner_ref().as_slice());
+
+        writer.flush();
+        assert_eq!([0, 1, 2, 3], writer.inner_ref().inner_ref().as_slice());
+
+        writer.write([4]);
+        writer.write([5]);
+        assert_eq!([0, 1, 2, 3], writer.inner_ref().inner_ref().as_slice());
+
+        writer.write([6]);
+        assert_eq!([0, 1, 2, 3, 4, 5],
+                   writer.inner_ref().inner_ref().as_slice());
+
+        writer.write([7, 8]);
+        assert_eq!([0, 1, 2, 3, 4, 5, 6],
+                   writer.inner_ref().inner_ref().as_slice());
+
+        writer.write([9, 10, 11]);
+        assert_eq!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                   writer.inner_ref().inner_ref().as_slice());
+
+        writer.flush();
+        assert_eq!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                   writer.inner_ref().inner_ref().as_slice());
+    }
+
+    // This is just here to make sure that we don't infinite loop in the
+    // newtype struct autoderef weirdness
+    #[test]
+    fn test_buffered_stream() {
+        use rt;
+        struct S;
+
+        impl io::Writer for S {
+            fn write(&mut self, _: &[u8]) {}
+        }
+
+        impl io::Reader for S {
+            fn read(&mut self, _: &mut [u8]) -> Option<uint> { None }
+            fn eof(&mut self) -> bool { true }
+        }
+
+        let mut stream = BufferedStream::new(S);
+        let mut buf = [];
+        stream.read(buf);
+        stream.eof();
+        stream.write(buf);
+        stream.flush();
+    }
+
+    #[test]
+    fn test_read_until() {
+        let inner = MemReader::new(~[0, 1, 2, 1, 0]);
+        let mut reader = BufferedReader::with_capacity(2, inner);
+        assert_eq!(reader.read_until(0), Some(~[0]));
+        assert_eq!(reader.read_until(2), Some(~[1, 2]));
+        assert_eq!(reader.read_until(1), Some(~[1]));
+        assert_eq!(reader.read_until(8), Some(~[0]));
+        assert_eq!(reader.read_until(9), None);
+    }
+
+    #[test]
+    fn test_line_buffer() {
+        let mut writer = LineBufferedWriter::new(MemWriter::new());
+        writer.write([0]);
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[]);
+        writer.write([1]);
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[]);
+        writer.flush();
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1]);
+        writer.write([0, '\n' as u8, 1]);
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1, 0, '\n' as u8]);
+        writer.flush();
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1, 0, '\n' as u8, 1]);
+    }
+}
diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs
new file mode 100644
index 00000000000..98dbec27fb9
--- /dev/null
+++ b/src/libstd/io/comm_adapters.rs
@@ -0,0 +1,57 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use option::Option;
+use comm::{GenericPort, GenericChan};
+use super::{Reader, Writer};
+
+struct PortReader<P>;
+
+impl<P: GenericPort<~[u8]>> PortReader<P> {
+    pub fn new(_port: P) -> PortReader<P> { fail!() }
+}
+
+impl<P: GenericPort<~[u8]>> Reader for PortReader<P> {
+    fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() }
+
+    fn eof(&mut self) -> bool { fail!() }
+}
+
+struct ChanWriter<C>;
+
+impl<C: GenericChan<~[u8]>> ChanWriter<C> {
+    pub fn new(_chan: C) -> ChanWriter<C> { fail!() }
+}
+
+impl<C: GenericChan<~[u8]>> Writer for ChanWriter<C> {
+    fn write(&mut self, _buf: &[u8]) { fail!() }
+}
+
+struct ReaderPort<R>;
+
+impl<R: Reader> ReaderPort<R> {
+    pub fn new(_reader: R) -> ReaderPort<R> { fail!() }
+}
+
+impl<R: Reader> GenericPort<~[u8]> for ReaderPort<R> {
+    fn recv(&self) -> ~[u8] { fail!() }
+
+    fn try_recv(&self) -> Option<~[u8]> { fail!() }
+}
+
+struct WriterChan<W>;
+
+impl<W: Writer> WriterChan<W> {
+    pub fn new(_writer: W) -> WriterChan<W> { fail!() }
+}
+
+impl<W: Writer> GenericChan<~[u8]> for WriterChan<W> {
+    fn send(&self, _x: ~[u8]) { fail!() }
+}
diff --git a/src/libstd/io/extensions.rs b/src/libstd/io/extensions.rs
new file mode 100644
index 00000000000..ebda2618dcf
--- /dev/null
+++ b/src/libstd/io/extensions.rs
@@ -0,0 +1,491 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Utility mixins that apply to all Readers and Writers
+
+// XXX: Not sure how this should be structured
+// XXX: Iteration should probably be considered separately
+
+use iter::Iterator;
+use option::Option;
+use io::{Reader, Decorator};
+
+/// An iterator that reads a single byte on each iteration,
+/// until `.read_byte()` returns `None`.
+///
+/// # Notes about the Iteration Protocol
+///
+/// The `ByteIterator` may yield `None` and thus terminate
+/// an iteration, but continue to yield elements if iteration
+/// is attempted again.
+///
+/// # Failure
+///
+/// Raises the same conditions as the `read` method, for
+/// each call to its `.next()` method.
+/// Yields `None` if the condition is handled.
+pub struct ByteIterator<T> {
+    priv reader: T,
+}
+
+impl<R: Reader> ByteIterator<R> {
+    pub fn new(r: R) -> ByteIterator<R> {
+        ByteIterator { reader: r }
+    }
+}
+
+impl<R> Decorator<R> for ByteIterator<R> {
+    fn inner(self) -> R { self.reader }
+    fn inner_ref<'a>(&'a self) -> &'a R { &self.reader }
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { &mut self.reader }
+}
+
+impl<'self, R: Reader> Iterator<u8> for ByteIterator<R> {
+    #[inline]
+    fn next(&mut self) -> Option<u8> {
+        self.reader.read_byte()
+    }
+}
+
+pub fn u64_to_le_bytes<T>(n: u64, size: uint,
+                          f: &fn(v: &[u8]) -> T) -> T {
+    assert!(size <= 8u);
+    match size {
+      1u => f(&[n as u8]),
+      2u => f(&[n as u8,
+              (n >> 8) as u8]),
+      4u => f(&[n as u8,
+              (n >> 8) as u8,
+              (n >> 16) as u8,
+              (n >> 24) as u8]),
+      8u => f(&[n as u8,
+              (n >> 8) as u8,
+              (n >> 16) as u8,
+              (n >> 24) as u8,
+              (n >> 32) as u8,
+              (n >> 40) as u8,
+              (n >> 48) as u8,
+              (n >> 56) as u8]),
+      _ => {
+
+        let mut bytes: ~[u8] = ~[];
+        let mut i = size;
+        let mut n = n;
+        while i > 0u {
+            bytes.push((n & 255_u64) as u8);
+            n >>= 8_u64;
+            i -= 1u;
+        }
+        f(bytes)
+      }
+    }
+}
+
+pub fn u64_to_be_bytes<T>(n: u64, size: uint,
+                           f: &fn(v: &[u8]) -> T) -> T {
+    assert!(size <= 8u);
+    match size {
+      1u => f(&[n as u8]),
+      2u => f(&[(n >> 8) as u8,
+              n as u8]),
+      4u => f(&[(n >> 24) as u8,
+              (n >> 16) as u8,
+              (n >> 8) as u8,
+              n as u8]),
+      8u => f(&[(n >> 56) as u8,
+              (n >> 48) as u8,
+              (n >> 40) as u8,
+              (n >> 32) as u8,
+              (n >> 24) as u8,
+              (n >> 16) as u8,
+              (n >> 8) as u8,
+              n as u8]),
+      _ => {
+        let mut bytes: ~[u8] = ~[];
+        let mut i = size;
+        while i > 0u {
+            let shift = ((i - 1u) * 8u) as u64;
+            bytes.push((n >> shift) as u8);
+            i -= 1u;
+        }
+        f(bytes)
+      }
+    }
+}
+
+pub fn u64_from_be_bytes(data: &[u8],
+                         start: uint,
+                         size: uint)
+                      -> u64 {
+    let mut sz = size;
+    assert!((sz <= 8u));
+    let mut val = 0_u64;
+    let mut pos = start;
+    while sz > 0u {
+        sz -= 1u;
+        val += (data[pos] as u64) << ((sz * 8u) as u64);
+        pos += 1u;
+    }
+    return val;
+}
+
+#[cfg(test)]
+mod test {
+    use option::{None, Option, Some};
+    use io::mem::{MemReader, MemWriter};
+    use io::{Reader, io_error, placeholder_error};
+    use vec::ImmutableVector;
+
+    struct InitialZeroByteReader {
+        count: int,
+    }
+
+    impl Reader for InitialZeroByteReader {
+        fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                Some(0)
+            } else {
+                buf[0] = 10;
+                Some(1)
+            }
+        }
+        fn eof(&mut self) -> bool {
+            false
+        }
+    }
+
+    struct EofReader;
+
+    impl Reader for EofReader {
+        fn read(&mut self, _: &mut [u8]) -> Option<uint> {
+            None
+        }
+        fn eof(&mut self) -> bool {
+            false
+        }
+    }
+
+    struct ErroringReader;
+
+    impl Reader for ErroringReader {
+        fn read(&mut self, _: &mut [u8]) -> Option<uint> {
+            io_error::cond.raise(placeholder_error());
+            None
+        }
+        fn eof(&mut self) -> bool {
+            false
+        }
+    }
+
+    struct PartialReader {
+        count: int,
+    }
+
+    impl Reader for PartialReader {
+        fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                buf[0] = 10;
+                buf[1] = 11;
+                Some(2)
+            } else {
+                buf[0] = 12;
+                buf[1] = 13;
+                Some(2)
+            }
+        }
+        fn eof(&mut self) -> bool {
+            false
+        }
+    }
+
+    struct ErroringLaterReader {
+        count: int,
+    }
+
+    impl Reader for ErroringLaterReader {
+        fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                buf[0] = 10;
+                Some(1)
+            } else {
+                io_error::cond.raise(placeholder_error());
+                None
+            }
+        }
+        fn eof(&mut self) -> bool {
+            false
+        }
+    }
+
+    struct ThreeChunkReader {
+        count: int,
+    }
+
+    impl Reader for ThreeChunkReader {
+        fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                buf[0] = 10;
+                buf[1] = 11;
+                Some(2)
+            } else if self.count == 1 {
+                self.count = 2;
+                buf[0] = 12;
+                buf[1] = 13;
+                Some(2)
+            } else {
+                None
+            }
+        }
+        fn eof(&mut self) -> bool {
+            false
+        }
+    }
+
+    #[test]
+    fn read_byte() {
+        let mut reader = MemReader::new(~[10]);
+        let byte = reader.read_byte();
+        assert!(byte == Some(10));
+    }
+
+    #[test]
+    fn read_byte_0_bytes() {
+        let mut reader = InitialZeroByteReader {
+            count: 0,
+        };
+        let byte = reader.read_byte();
+        assert!(byte == Some(10));
+    }
+
+    #[test]
+    fn read_byte_eof() {
+        let mut reader = EofReader;
+        let byte = reader.read_byte();
+        assert!(byte == None);
+    }
+
+    #[test]
+    fn read_byte_error() {
+        let mut reader = ErroringReader;
+        do io_error::cond.trap(|_| {
+        }).inside {
+            let byte = reader.read_byte();
+            assert!(byte == None);
+        }
+    }
+
+    #[test]
+    fn bytes_0_bytes() {
+        let reader = InitialZeroByteReader {
+            count: 0,
+        };
+        let byte = reader.bytes().next();
+        assert!(byte == Some(10));
+    }
+
+    #[test]
+    fn bytes_eof() {
+        let reader = EofReader;
+        let byte = reader.bytes().next();
+        assert!(byte == None);
+    }
+
+    #[test]
+    fn bytes_error() {
+        let reader = ErroringReader;
+        let mut it = reader.bytes();
+        do io_error::cond.trap(|_| ()).inside {
+            let byte = it.next();
+            assert!(byte == None);
+        }
+    }
+
+    #[test]
+    fn read_bytes() {
+        let mut reader = MemReader::new(~[10, 11, 12, 13]);
+        let bytes = reader.read_bytes(4);
+        assert!(bytes == ~[10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn read_bytes_partial() {
+        let mut reader = PartialReader {
+            count: 0,
+        };
+        let bytes = reader.read_bytes(4);
+        assert!(bytes == ~[10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn read_bytes_eof() {
+        let mut reader = MemReader::new(~[10, 11]);
+        do io_error::cond.trap(|_| {
+        }).inside {
+            assert!(reader.read_bytes(4) == ~[10, 11]);
+        }
+    }
+
+    #[test]
+    fn push_bytes() {
+        let mut reader = MemReader::new(~[10, 11, 12, 13]);
+        let mut buf = ~[8, 9];
+        reader.push_bytes(&mut buf, 4);
+        assert!(buf == ~[8, 9, 10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn push_bytes_partial() {
+        let mut reader = PartialReader {
+            count: 0,
+        };
+        let mut buf = ~[8, 9];
+        reader.push_bytes(&mut buf, 4);
+        assert!(buf == ~[8, 9, 10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn push_bytes_eof() {
+        let mut reader = MemReader::new(~[10, 11]);
+        let mut buf = ~[8, 9];
+        do io_error::cond.trap(|_| {
+        }).inside {
+            reader.push_bytes(&mut buf, 4);
+            assert!(buf == ~[8, 9, 10, 11]);
+        }
+    }
+
+    #[test]
+    fn push_bytes_error() {
+        let mut reader = ErroringLaterReader {
+            count: 0,
+        };
+        let mut buf = ~[8, 9];
+        do io_error::cond.trap(|_| { } ).inside {
+            reader.push_bytes(&mut buf, 4);
+        }
+        assert!(buf == ~[8, 9, 10]);
+    }
+
+    #[test]
+    #[should_fail]
+    fn push_bytes_fail_reset_len() {
+        // push_bytes unsafely sets the vector length. This is testing that
+        // upon failure the length is reset correctly.
+        let mut reader = ErroringLaterReader {
+            count: 0,
+        };
+        let buf = @mut ~[8, 9];
+        do (|| {
+            reader.push_bytes(&mut *buf, 4);
+        }).finally {
+            // NB: Using rtassert here to trigger abort on failure since this is a should_fail test
+            // FIXME: #7049 This fails because buf is still borrowed
+            //rtassert!(*buf == ~[8, 9, 10]);
+        }
+    }
+
+    #[test]
+    fn read_to_end() {
+        let mut reader = ThreeChunkReader {
+            count: 0,
+        };
+        let buf = reader.read_to_end();
+        assert!(buf == ~[10, 11, 12, 13]);
+    }
+
+    #[test]
+    #[should_fail]
+    fn read_to_end_error() {
+        let mut reader = ThreeChunkReader {
+            count: 0,
+        };
+        let buf = reader.read_to_end();
+        assert!(buf == ~[10, 11]);
+    }
+
+    #[test]
+    fn test_read_write_le_mem() {
+        let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::max_value];
+
+        let mut writer = MemWriter::new();
+        for i in uints.iter() {
+            writer.write_le_u64(*i);
+        }
+
+        let mut reader = MemReader::new(writer.inner());
+        for i in uints.iter() {
+            assert!(reader.read_le_u64() == *i);
+        }
+    }
+
+
+    #[test]
+    fn test_read_write_be() {
+        let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::max_value];
+
+        let mut writer = MemWriter::new();
+        for i in uints.iter() {
+            writer.write_be_u64(*i);
+        }
+
+        let mut reader = MemReader::new(writer.inner());
+        for i in uints.iter() {
+            assert!(reader.read_be_u64() == *i);
+        }
+    }
+
+    #[test]
+    fn test_read_be_int_n() {
+        let ints = [::i32::min_value, -123456, -42, -5, 0, 1, ::i32::max_value];
+
+        let mut writer = MemWriter::new();
+        for i in ints.iter() {
+            writer.write_be_i32(*i);
+        }
+
+        let mut reader = MemReader::new(writer.inner());
+        for i in ints.iter() {
+            // this tests that the sign extension is working
+            // (comparing the values as i32 would not test this)
+            assert!(reader.read_be_int_n(4) == *i as i64);
+        }
+    }
+
+    #[test]
+    fn test_read_f32() {
+        //big-endian floating-point 8.1250
+        let buf = ~[0x41, 0x02, 0x00, 0x00];
+
+        let mut writer = MemWriter::new();
+        writer.write(buf);
+
+        let mut reader = MemReader::new(writer.inner());
+        let f = reader.read_be_f32();
+        assert!(f == 8.1250);
+    }
+
+    #[test]
+    fn test_read_write_f32() {
+        let f:f32 = 8.1250;
+
+        let mut writer = MemWriter::new();
+        writer.write_be_f32(f);
+        writer.write_le_f32(f);
+
+        let mut reader = MemReader::new(writer.inner());
+        assert!(reader.read_be_f32() == 8.1250);
+        assert!(reader.read_le_f32() == 8.1250);
+    }
+
+}
diff --git a/src/libstd/io/flate.rs b/src/libstd/io/flate.rs
new file mode 100644
index 00000000000..8a5aa171eb8
--- /dev/null
+++ b/src/libstd/io/flate.rs
@@ -0,0 +1,123 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Some various other I/O types
+
+// FIXME(#3660): should move to libextra
+
+use prelude::*;
+use super::*;
+
+/// A Writer decorator that compresses using the 'deflate' scheme
+pub struct DeflateWriter<W> {
+    priv inner_writer: W
+}
+
+impl<W: Writer> DeflateWriter<W> {
+    pub fn new(inner_writer: W) -> DeflateWriter<W> {
+        DeflateWriter {
+            inner_writer: inner_writer
+        }
+    }
+}
+
+impl<W: Writer> Writer for DeflateWriter<W> {
+    fn write(&mut self, _buf: &[u8]) { fail!() }
+
+    fn flush(&mut self) { fail!() }
+}
+
+impl<W: Writer> Decorator<W> for DeflateWriter<W> {
+    fn inner(self) -> W {
+        match self {
+            DeflateWriter { inner_writer: w } => w
+        }
+    }
+
+    fn inner_ref<'a>(&'a self) -> &'a W {
+        match *self {
+            DeflateWriter { inner_writer: ref w } => w
+        }
+    }
+
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W {
+        match *self {
+            DeflateWriter { inner_writer: ref mut w } => w
+        }
+    }
+}
+
+/// A Reader decorator that decompresses using the 'deflate' scheme
+pub struct InflateReader<R> {
+    priv inner_reader: R
+}
+
+impl<R: Reader> InflateReader<R> {
+    pub fn new(inner_reader: R) -> InflateReader<R> {
+        InflateReader {
+            inner_reader: inner_reader
+        }
+    }
+}
+
+impl<R: Reader> Reader for InflateReader<R> {
+    fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() }
+
+    fn eof(&mut self) -> bool { fail!() }
+}
+
+impl<R: Reader> Decorator<R> for InflateReader<R> {
+    fn inner(self) -> R {
+        match self {
+            InflateReader { inner_reader: r } => r
+        }
+    }
+
+    fn inner_ref<'a>(&'a self) -> &'a R {
+        match *self {
+            InflateReader { inner_reader: ref r } => r
+        }
+    }
+
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R {
+        match *self {
+            InflateReader { inner_reader: ref mut r } => r
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::*;
+    use super::*;
+    use super::super::mem::*;
+    use super::super::Decorator;
+
+    use str;
+
+    #[test]
+    #[ignore]
+    fn smoke_test() {
+        let mem_writer = MemWriter::new();
+        let mut deflate_writer = DeflateWriter::new(mem_writer);
+        let in_msg = "test";
+        let in_bytes = in_msg.as_bytes();
+        deflate_writer.write(in_bytes);
+        deflate_writer.flush();
+        let buf = deflate_writer.inner().inner();
+        let mem_reader = MemReader::new(buf);
+        let mut inflate_reader = InflateReader::new(mem_reader);
+        let mut out_bytes = [0, .. 100];
+        let bytes_read = inflate_reader.read(out_bytes).unwrap();
+        assert_eq!(bytes_read, in_bytes.len());
+        let out_msg = str::from_utf8(out_bytes);
+        assert!(in_msg == out_msg);
+    }
+}
diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs
new file mode 100644
index 00000000000..d5514eb14d3
--- /dev/null
+++ b/src/libstd/io/fs.rs
@@ -0,0 +1,1289 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*! Synchronous File I/O
+
+This module provides a set of functions and traits for working
+with regular files & directories on a filesystem.
+
+At the top-level of the module are a set of freestanding functions, associated
+with various filesystem operations. They all operate on a `Path` object.
+
+All operations in this module, including those as part of `File` et al
+block the task during execution. Most will raise `std::io::io_error`
+conditions in the event of failure.
+
+Also included in this module is an implementation block on the `Path` object
+defined in `std::path::Path`. The impl adds useful methods about inspecting the
+metadata of a file. This includes getting the `stat` information, reading off
+particular bits of it, etc.
+
+# Example
+
+    use std::io::{File, fs};
+
+    let path = Path::new("foo.txt");
+
+    // create the file, whether it exists or not
+    let mut file = File::create(&path);
+    file.write(bytes!("foobar"));
+
+    // open the file in read-only mode
+    let mut file = File::open(&path);
+    file.read_to_end();
+
+    println!("{}", path.stat().size);
+    fs::symlink(&path, &Path::new("bar.txt"));
+    fs::unlink(&path);
+
+*/
+
+use c_str::ToCStr;
+use clone::Clone;
+use iter::Iterator;
+use super::{Reader, Writer, Seek};
+use super::{SeekStyle, Read, Write, Open, IoError, Truncate,
+            FileMode, FileAccess, FileStat, io_error, FilePermission};
+use rt::rtio::{RtioFileStream, IoFactory, with_local_io};
+use io;
+use option::{Some, None, Option};
+use result::{Ok, Err, Result};
+use path;
+use path::{Path, GenericPath};
+use vec::{OwnedVector, ImmutableVector};
+
+/// Unconstrained file access type that exposes read and write operations
+///
+/// Can be constructed via `File::open()`, `File::create()`, and
+/// `File::open_mode()`.
+///
+/// # Errors
+///
+/// This type will raise an io_error condition if operations are attempted against
+/// it for which its underlying file descriptor was not configured at creation
+/// time, via the `FileAccess` parameter to `File::open_mode()`.
+pub struct File {
+    priv fd: ~RtioFileStream,
+    priv path: Path,
+    priv last_nread: int,
+}
+
+fn io_raise<T>(f: &fn(io: &mut IoFactory) -> Result<T, IoError>) -> Option<T> {
+    do with_local_io |io| {
+        match f(io) {
+            Ok(t) => Some(t),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+impl File {
+    /// Open a file at `path` in the mode specified by the `mode` and `access`
+    /// arguments
+    ///
+    /// # Example
+    ///
+    ///     use std::io::{File, io_error, Open, ReadWrite};
+    ///
+    ///     let p = Path::new("/some/file/path.txt");
+    ///
+    ///     do io_error::cond.trap(|_| {
+    ///         // hoo-boy...
+    ///     }).inside {
+    ///         let file = match File::open_mode(&p, Open, ReadWrite) {
+    ///             Some(s) => s,
+    ///             None => fail!("whoops! I'm sure this raised, anyways..")
+    ///         };
+    ///         // do some stuff with that file
+    ///
+    ///         // the file will be closed at the end of this block
+    ///     }
+    ///     // ..
+    ///
+    /// `FileMode` and `FileAccess` provide information about the permissions
+    /// context in which a given stream is created. More information about them
+    /// can be found in `std::io`'s docs. If a file is opened with `Write`
+    /// or `ReadWrite` access, then it will be created it it does not already
+    /// exist.
+    ///
+    /// Note that, with this function, a `File` is returned regardless of the
+    /// access-limitations indicated by `FileAccess` (e.g. calling `write` on a
+    /// `File` opened as `Read` will raise an `io_error` condition at runtime).
+    ///
+    /// # Errors
+    ///
+    /// This function will raise an `io_error` condition under a number of
+    /// different circumstances, to include but not limited to:
+    ///
+    /// * Opening a file that does not exist with `Read` access.
+    /// * Attempting to open a file with a `FileAccess` that the user lacks
+    ///   permissions for
+    /// * Filesystem-level errors (full disk, etc)
+    pub fn open_mode(path: &Path,
+                     mode: FileMode,
+                     access: FileAccess) -> Option<File> {
+        do with_local_io |io| {
+            match io.fs_open(&path.to_c_str(), mode, access) {
+                Ok(fd) => Some(File {
+                    path: path.clone(),
+                    fd: fd,
+                    last_nread: -1
+                }),
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }
+    }
+
+    /// Attempts to open a file in read-only mode. This function is equivalent to
+    /// `File::open_mode(path, Open, Read)`, and will raise all of the same
+    /// errors that `File::open_mode` does.
+    ///
+    /// For more information, see the `File::open_mode` function.
+    ///
+    /// # Example
+    ///
+    ///     use std::io::File;
+    ///
+    ///     let contents = File::open(&Path::new("foo.txt")).read_to_end();
+    pub fn open(path: &Path) -> Option<File> {
+        File::open_mode(path, Open, Read)
+    }
+
+    /// Attempts to create a file in write-only mode. This function is
+    /// equivalent to `File::open_mode(path, Truncate, Write)`, and will
+    /// raise all of the same errors that `File::open_mode` does.
+    ///
+    /// For more information, see the `File::open_mode` function.
+    ///
+    /// # Example
+    ///
+    ///     use std::io::File;
+    ///
+    ///     let mut f = File::create(&Path::new("foo.txt"));
+    ///     f.write(bytes!("This is a sample file"));
+    pub fn create(path: &Path) -> Option<File> {
+        File::open_mode(path, Truncate, Write)
+    }
+
+    /// Returns the original path which was used to open this file.
+    pub fn path<'a>(&'a self) -> &'a Path {
+        &self.path
+    }
+
+    /// Synchronizes all modifications to this file to its permanent storage
+    /// device. This will flush any internal buffers necessary to perform this
+    /// operation.
+    ///
+    /// # Errors
+    ///
+    /// This function will raise on the `io_error` condition on failure.
+    pub fn fsync(&mut self) {
+        self.fd.fsync();
+    }
+
+    /// This function is similar to `fsync`, except that it may not synchronize
+    /// file metadata to the filesystem. This is intended for use case which
+    /// must synchronize content, but don't need the metadata on disk. The goal
+    /// of this method is to reduce disk operations.
+    ///
+    /// # Errors
+    ///
+    /// This function will raise on the `io_error` condition on failure.
+    pub fn datasync(&mut self) {
+        self.fd.datasync();
+    }
+
+    /// Either truncates or extends the underlying file, as extended from the
+    /// file's current position. This is equivalent to the unix `truncate`
+    /// function.
+    ///
+    /// The offset given is added to the file's current position and the result
+    /// is the new size of the file. If the new size is less than the current
+    /// size, then the file is truncated. If the new size is greater than the
+    /// current size, then the file is expanded to be filled with 0s.
+    ///
+    /// # Errors
+    ///
+    /// On error, this function will raise on the `io_error` condition.
+    pub fn truncate(&mut self, offset: i64) {
+        self.fd.truncate(offset);
+    }
+}
+
+/// Unlink a file from the underlying filesystem.
+///
+/// # Example
+///
+///     use std::io::fs;
+///
+///     let p = Path::new("/some/file/path.txt");
+///     fs::unlink(&p);
+///     // if we made it here without failing, then the
+///     // unlink operation was successful
+///
+/// Note that, just because an unlink call was successful, it is not
+/// guaranteed that a file is immediately deleted (e.g. depending on
+/// platform, other open file descriptors may prevent immediate removal)
+///
+/// # Errors
+///
+/// This function will raise an `io_error` condition if the path points to a
+/// directory, the user lacks permissions to remove the file, or if some
+/// other filesystem-level error occurs.
+pub fn unlink(path: &Path) {
+    do io_raise |io| { io.fs_unlink(&path.to_c_str()) };
+}
+
+/// Given a path, query the file system to get information about a file,
+/// directory, etc. This function will traverse symlinks to query
+/// information about the destination file.
+///
+/// Returns a fully-filled out stat structure on succes, and on failure it
+/// will return a dummy stat structure (it is expected that the condition
+/// raised is handled as well).
+///
+/// # Example
+///
+///     use std::io;
+///     use std::io::fs;
+///
+///     let p = Path::new("/some/file/path.txt");
+///     match io::result(|| fs::stat(&p)) {
+///         Ok(stat) => { /* ... */ }
+///         Err(e) => { /* handle error */ }
+///     }
+///
+/// # Errors
+///
+/// This call will raise an `io_error` condition if the user lacks the
+/// requisite permissions to perform a `stat` call on the given path or if
+/// there is no entry in the filesystem at the provided path.
+pub fn stat(path: &Path) -> FileStat {
+    do io_raise |io| {
+        io.fs_stat(&path.to_c_str())
+    }.unwrap_or_else(dummystat)
+}
+
+fn dummystat() -> FileStat {
+    FileStat {
+        path: Path::new(""),
+        size: 0,
+        kind: io::TypeFile,
+        perm: 0,
+        created: 0,
+        modified: 0,
+        accessed: 0,
+        unstable: io::UnstableFileStat {
+            device: 0,
+            inode: 0,
+            rdev: 0,
+            nlink: 0,
+            uid: 0,
+            gid: 0,
+            blksize: 0,
+            blocks: 0,
+            flags: 0,
+            gen: 0,
+        }
+    }
+}
+
+/// Perform the same operation as the `stat` function, except that this
+/// function does not traverse through symlinks. This will return
+/// information about the symlink file instead of the file that it points
+/// to.
+///
+/// # Errors
+///
+/// See `stat`
+pub fn lstat(path: &Path) -> FileStat {
+    do io_raise |io| {
+        io.fs_lstat(&path.to_c_str())
+    }.unwrap_or_else(dummystat)
+}
+
+/// Rename a file or directory to a new name.
+///
+/// # Example
+///
+///     use std::io::fs;
+///
+///     fs::rename(&Path::new("foo"), &Path::new("bar"));
+///     // Oh boy, nothing was raised!
+///
+/// # Errors
+///
+/// Will raise an `io_error` condition if the provided `path` doesn't exist,
+/// the process lacks permissions to view the contents, or if some other
+/// intermittent I/O error occurs.
+pub fn rename(from: &Path, to: &Path) {
+    do io_raise |io| {
+        io.fs_rename(&from.to_c_str(), &to.to_c_str())
+    };
+}
+
+/// Copies the contents of one file to another. This function will also
+/// copy the permission bits of the original file to the destination file.
+///
+/// Note that if `from` and `to` both point to the same file, then the file
+/// will likely get truncated by this operation.
+///
+/// # Example
+///
+///     use std::io::fs;
+///
+///     fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt"));
+///     // Oh boy, nothing was raised!
+///
+/// # Errors
+///
+/// Will raise an `io_error` condition is the following situtations, but is
+/// not limited to just these cases:
+///
+/// * The `from` path is not a file
+/// * The `from` file does not exist
+/// * The current process does not have the permission rights to access
+///   `from` or write `to`
+///
+/// Note that this copy is not atomic in that once the destination is
+/// ensured to not exist, there is nothing preventing the destination from
+/// being created and then destroyed by this operation.
+pub fn copy(from: &Path, to: &Path) {
+    if !from.is_file() {
+        return io_error::cond.raise(IoError {
+            kind: io::MismatchedFileTypeForOperation,
+            desc: "the source path is not an existing file",
+            detail: None,
+        });
+    }
+
+    let mut reader = match File::open(from) { Some(f) => f, None => return };
+    let mut writer = match File::create(to) { Some(f) => f, None => return };
+    let mut buf = [0, ..io::DEFAULT_BUF_SIZE];
+
+    loop {
+        match reader.read(buf) {
+            Some(amt) => writer.write(buf.slice_to(amt)),
+            None => break
+        }
+    }
+
+    chmod(to, from.stat().perm)
+}
+
+/// Changes the permission mode bits found on a file or a directory. This
+/// function takes a mask from the `io` module
+///
+/// # Example
+///
+///     use std::io;
+///     use std::io::fs;
+///
+///     fs::chmod(&Path::new("file.txt"), io::UserFile);
+///     fs::chmod(&Path::new("file.txt"), io::UserRead | io::UserWrite);
+///     fs::chmod(&Path::new("dir"),      io::UserDir);
+///     fs::chmod(&Path::new("file.exe"), io::UserExec);
+///
+/// # Errors
+///
+/// If this funciton encounters an I/O error, it will raise on the `io_error`
+/// condition. Some possible error situations are not having the permission to
+/// change the attributes of a file or the file not existing.
+pub fn chmod(path: &Path, mode: io::FilePermission) {
+    do io_raise |io| {
+        io.fs_chmod(&path.to_c_str(), mode)
+    };
+}
+
+/// Change the user and group owners of a file at the specified path.
+///
+/// # Errors
+///
+/// This funtion will raise on the `io_error` condition on failure.
+pub fn chown(path: &Path, uid: int, gid: int) {
+    do io_raise |io| { io.fs_chown(&path.to_c_str(), uid, gid) };
+}
+
+/// Creates a new hard link on the filesystem. The `dst` path will be a
+/// link pointing to the `src` path. Note that systems often require these
+/// two paths to both be located on the same filesystem.
+///
+/// # Errors
+///
+/// This function will raise on the `io_error` condition on failure.
+pub fn link(src: &Path, dst: &Path) {
+    do io_raise |io| { io.fs_link(&src.to_c_str(), &dst.to_c_str()) };
+}
+
+/// Creates a new symbolic link on the filesystem. The `dst` path will be a
+/// symlink pointing to the `src` path.
+///
+/// # Errors
+///
+/// This function will raise on the `io_error` condition on failure.
+pub fn symlink(src: &Path, dst: &Path) {
+    do io_raise |io| { io.fs_symlink(&src.to_c_str(), &dst.to_c_str()) };
+}
+
+/// Reads a symlink, returning the file that the symlink points to.
+///
+/// # Errors
+///
+/// This function will raise on the `io_error` condition on failure. Failure
+/// conditions include reading a file that does not exist or reading a file
+/// which is not a symlink.
+pub fn readlink(path: &Path) -> Option<Path> {
+    do io_raise |io| { io.fs_readlink(&path.to_c_str()) }
+}
+
+/// Create a new, empty directory at the provided path
+///
+/// # Example
+///
+///     use std::libc::S_IRWXU;
+///     use std::io::fs;
+///
+///     let p = Path::new("/some/dir");
+///     fs::mkdir(&p, S_IRWXU as int);
+///     // If we got here, our directory exists! Horray!
+///
+/// # Errors
+///
+/// This call will raise an `io_error` condition if the user lacks permissions
+/// to make a new directory at the provided path, or if the directory already
+/// exists.
+pub fn mkdir(path: &Path, mode: FilePermission) {
+    do io_raise |io| {
+        io.fs_mkdir(&path.to_c_str(), mode)
+    };
+}
+
+/// Remove an existing, empty directory
+///
+/// # Example
+///
+///     use std::io::fs;
+///
+///     let p = Path::new("/some/dir");
+///     fs::rmdir(&p);
+///     // good riddance, you mean ol' directory
+///
+/// # Errors
+///
+/// This call will raise an `io_error` condition if the user lacks permissions
+/// to remove the directory at the provided path, or if the directory isn't
+/// empty.
+pub fn rmdir(path: &Path) {
+    do io_raise |io| {
+        io.fs_rmdir(&path.to_c_str())
+    };
+}
+
+/// Retrieve a vector containing all entries within a provided directory
+///
+/// # Example
+///
+///     use std::io::fs;
+///
+///     // one possible implementation of fs::walk_dir only visiting files
+///     fn visit_dirs(dir: &Path, cb: &fn(&Path)) {
+///         if dir.is_dir() {
+///             let contents = fs::readdir(dir).unwrap();
+///             for entry in contents.iter() {
+///                 if entry.is_dir() { visit_dirs(entry, cb); }
+///                 else { cb(entry); }
+///             }
+///         }
+///         else { fail!("nope"); }
+///     }
+///
+/// # Errors
+///
+/// Will raise an `io_error` condition if the provided `from` doesn't exist,
+/// the process lacks permissions to view the contents or if the `path` points
+/// at a non-directory file
+pub fn readdir(path: &Path) -> ~[Path] {
+    do io_raise |io| {
+        io.fs_readdir(&path.to_c_str(), 0)
+    }.unwrap_or_else(|| ~[])
+}
+
+/// Returns an iterator which will recursively walk the directory structure
+/// rooted at `path`. The path given will not be iterated over, and this will
+/// perform iteration in a top-down order.
+pub fn walk_dir(path: &Path) -> WalkIterator {
+    WalkIterator { stack: readdir(path) }
+}
+
+/// An iterator which walks over a directory
+pub struct WalkIterator {
+    priv stack: ~[Path],
+}
+
+impl Iterator<Path> for WalkIterator {
+    fn next(&mut self) -> Option<Path> {
+        match self.stack.shift_opt() {
+            Some(path) => {
+                if path.is_dir() {
+                    self.stack.push_all_move(readdir(&path));
+                }
+                Some(path)
+            }
+            None => None
+        }
+    }
+}
+
+/// Recursively create a directory and all of its parent components if they
+/// are missing.
+///
+/// # Errors
+///
+/// This function will raise on the `io_error` condition if an error
+/// happens, see `fs::mkdir` for more information about error conditions
+/// and performance.
+pub fn mkdir_recursive(path: &Path, mode: FilePermission) {
+    // tjc: if directory exists but with different permissions,
+    // should we return false?
+    if path.is_dir() {
+        return
+    }
+    if path.filename().is_some() {
+        mkdir_recursive(&path.dir_path(), mode);
+    }
+    mkdir(path, mode)
+}
+
+/// Removes a directory at this path, after removing all its contents. Use
+/// carefully!
+///
+/// # Errors
+///
+/// This function will raise on the `io_error` condition if an error
+/// happens. See `file::unlink` and `fs::readdir` for possible error
+/// conditions.
+pub fn rmdir_recursive(path: &Path) {
+    let children = readdir(path);
+    for child in children.iter() {
+        if child.is_dir() {
+            rmdir_recursive(child);
+        } else {
+            unlink(child);
+        }
+    }
+    // Directory should now be empty
+    rmdir(path);
+}
+
+/// Changes the timestamps for a file's last modification and access time.
+/// The file at the path specified will have its last access time set to
+/// `atime` and its modification time set to `mtime`. The times specified should
+/// be in milliseconds.
+///
+/// # Errors
+///
+/// This function will raise on the `io_error` condition if an error
+/// happens.
+// FIXME(#10301) these arguments should not be u64
+pub fn change_file_times(path: &Path, atime: u64, mtime: u64) {
+    do io_raise |io| {
+        io.fs_utime(&path.to_c_str(), atime, mtime)
+    };
+}
+
+impl Reader for File {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        match self.fd.read(buf) {
+            Ok(read) => {
+                self.last_nread = read;
+                match read {
+                    0 => None,
+                    _ => Some(read as uint)
+                }
+            },
+            Err(ioerr) => {
+                // EOF is indicated by returning None
+                if ioerr.kind != io::EndOfFile {
+                    io_error::cond.raise(ioerr);
+                }
+                return None;
+            }
+        }
+    }
+
+    fn eof(&mut self) -> bool { self.last_nread == 0 }
+}
+
+impl Writer for File {
+    fn write(&mut self, buf: &[u8]) {
+        match self.fd.write(buf) {
+            Ok(()) => (),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+            }
+        }
+    }
+}
+
+impl Seek for File {
+    fn tell(&self) -> u64 {
+        let res = self.fd.tell();
+        match res {
+            Ok(cursor) => cursor,
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+                return -1;
+            }
+        }
+    }
+
+    fn seek(&mut self, pos: i64, style: SeekStyle) {
+        match self.fd.seek(pos, style) {
+            Ok(_) => {
+                // successful seek resets EOF indicator
+                self.last_nread = -1;
+                ()
+            },
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+            }
+        }
+    }
+}
+
+impl path::Path {
+    /// Get information on the file, directory, etc at this path.
+    ///
+    /// Consult the `file::stat` documentation for more info.
+    ///
+    /// This call preserves identical runtime/error semantics with `file::stat`.
+    pub fn stat(&self) -> FileStat { stat(self) }
+
+    /// Boolean value indicator whether the underlying file exists on the local
+    /// filesystem. This will return true if the path points to either a
+    /// directory or a file.
+    ///
+    /// # Errors
+    ///
+    /// Will not raise a condition
+    pub fn exists(&self) -> bool {
+        io::result(|| self.stat()).is_ok()
+    }
+
+    /// Whether the underlying implemention (be it a file path, or something
+    /// else) points at a "regular file" on the FS. Will return false for paths
+    /// to non-existent locations or directories or other non-regular files
+    /// (named pipes, etc).
+    ///
+    /// # Errors
+    ///
+    /// Will not raise a condition
+    pub fn is_file(&self) -> bool {
+        match io::result(|| self.stat()) {
+            Ok(s) => s.kind == io::TypeFile,
+            Err(*) => false
+        }
+    }
+
+    /// Whether the underlying implemention (be it a file path,
+    /// or something else) is pointing at a directory in the underlying FS.
+    /// Will return false for paths to non-existent locations or if the item is
+    /// not a directory (eg files, named pipes, links, etc)
+    ///
+    /// # Errors
+    ///
+    /// Will not raise a condition
+    pub fn is_dir(&self) -> bool {
+        match io::result(|| self.stat()) {
+            Ok(s) => s.kind == io::TypeDirectory,
+            Err(*) => false
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::*;
+    use io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, ReadWrite};
+    use io;
+    use str;
+    use super::{File, rmdir, mkdir, readdir, rmdir_recursive, mkdir_recursive,
+                copy, unlink, stat, symlink, link, readlink, chmod,
+                lstat, change_file_times};
+
+    fn tmpdir() -> Path {
+        use os;
+        use rand;
+        let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>()));
+        mkdir(&ret, io::UserRWX);
+        ret
+    }
+
+    fn free<T>(_: T) {}
+
+    #[test]
+    fn file_test_io_smoke_test() {
+        let message = "it's alright. have a good time";
+        let filename = &Path::new("./tmp/file_rt_io_file_test.txt");
+        {
+            let mut write_stream = File::open_mode(filename, Open, ReadWrite);
+            write_stream.write(message.as_bytes());
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            let mut read_buf = [0, .. 1028];
+            let read_str = match read_stream.read(read_buf).unwrap() {
+                -1|0 => fail!("shouldn't happen"),
+                n => str::from_utf8(read_buf.slice_to(n))
+            };
+            assert!(read_str == message.to_owned());
+        }
+        unlink(filename);
+    }
+
+    #[test]
+    fn file_test_io_invalid_path_opened_without_create_should_raise_condition() {
+        let filename = &Path::new("./tmp/file_that_does_not_exist.txt");
+        let mut called = false;
+        do io_error::cond.trap(|_| {
+            called = true;
+        }).inside {
+            let result = File::open_mode(filename, Open, Read);
+            assert!(result.is_none());
+        }
+        assert!(called);
+    }
+
+    #[test]
+    fn file_test_iounlinking_invalid_path_should_raise_condition() {
+        let filename = &Path::new("./tmp/file_another_file_that_does_not_exist.txt");
+        let mut called = false;
+        do io_error::cond.trap(|_| {
+            called = true;
+        }).inside {
+            unlink(filename);
+        }
+        assert!(called);
+    }
+
+    #[test]
+    fn file_test_io_non_positional_read() {
+        let message = "ten-four";
+        let mut read_mem = [0, .. 8];
+        let filename = &Path::new("./tmp/file_rt_io_file_test_positional.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            rw_stream.write(message.as_bytes());
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            {
+                let read_buf = read_mem.mut_slice(0, 4);
+                read_stream.read(read_buf);
+            }
+            {
+                let read_buf = read_mem.mut_slice(4, 8);
+                read_stream.read(read_buf);
+            }
+        }
+        unlink(filename);
+        let read_str = str::from_utf8(read_mem);
+        assert!(read_str == message.to_owned());
+    }
+
+    #[test]
+    fn file_test_io_seek_and_tell_smoke_test() {
+        let message = "ten-four";
+        let mut read_mem = [0, .. 4];
+        let set_cursor = 4 as u64;
+        let mut tell_pos_pre_read;
+        let mut tell_pos_post_read;
+        let filename = &Path::new("./tmp/file_rt_io_file_test_seeking.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            rw_stream.write(message.as_bytes());
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            read_stream.seek(set_cursor as i64, SeekSet);
+            tell_pos_pre_read = read_stream.tell();
+            read_stream.read(read_mem);
+            tell_pos_post_read = read_stream.tell();
+        }
+        unlink(filename);
+        let read_str = str::from_utf8(read_mem);
+        assert!(read_str == message.slice(4, 8).to_owned());
+        assert!(tell_pos_pre_read == set_cursor);
+        assert!(tell_pos_post_read == message.len() as u64);
+    }
+
+    #[test]
+    fn file_test_io_seek_and_write() {
+        let initial_msg =   "food-is-yummy";
+        let overwrite_msg =    "-the-bar!!";
+        let final_msg =     "foo-the-bar!!";
+        let seek_idx = 3;
+        let mut read_mem = [0, .. 13];
+        let filename = &Path::new("./tmp/file_rt_io_file_test_seek_and_write.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            rw_stream.write(initial_msg.as_bytes());
+            rw_stream.seek(seek_idx as i64, SeekSet);
+            rw_stream.write(overwrite_msg.as_bytes());
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            read_stream.read(read_mem);
+        }
+        unlink(filename);
+        let read_str = str::from_utf8(read_mem);
+        assert!(read_str == final_msg.to_owned());
+    }
+
+    #[test]
+    fn file_test_io_seek_shakedown() {
+        use std::str;          // 01234567890123
+        let initial_msg =   "qwer-asdf-zxcv";
+        let chunk_one = "qwer";
+        let chunk_two = "asdf";
+        let chunk_three = "zxcv";
+        let mut read_mem = [0, .. 4];
+        let filename = &Path::new("./tmp/file_rt_io_file_test_seek_shakedown.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            rw_stream.write(initial_msg.as_bytes());
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+
+            read_stream.seek(-4, SeekEnd);
+            read_stream.read(read_mem);
+            let read_str = str::from_utf8(read_mem);
+            assert!(read_str == chunk_three.to_owned());
+
+            read_stream.seek(-9, SeekCur);
+            read_stream.read(read_mem);
+            let read_str = str::from_utf8(read_mem);
+            assert!(read_str == chunk_two.to_owned());
+
+            read_stream.seek(0, SeekSet);
+            read_stream.read(read_mem);
+            let read_str = str::from_utf8(read_mem);
+            assert!(read_str == chunk_one.to_owned());
+        }
+        unlink(filename);
+    }
+
+    #[test]
+    fn file_test_stat_is_correct_on_is_file() {
+        let filename = &Path::new("./tmp/file_stat_correct_on_is_file.txt");
+        {
+            let mut fs = File::open_mode(filename, Open, ReadWrite);
+            let msg = "hw";
+            fs.write(msg.as_bytes());
+        }
+        let stat_res = stat(filename);
+        assert_eq!(stat_res.kind, io::TypeFile);
+        unlink(filename);
+    }
+
+    #[test]
+    fn file_test_stat_is_correct_on_is_dir() {
+        let filename = &Path::new("./tmp/file_stat_correct_on_is_dir");
+        mkdir(filename, io::UserRWX);
+        let stat_res = filename.stat();
+        assert!(stat_res.kind == io::TypeDirectory);
+        rmdir(filename);
+    }
+
+    #[test]
+    fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
+        let dir = &Path::new("./tmp/fileinfo_false_on_dir");
+        mkdir(dir, io::UserRWX);
+        assert!(dir.is_file() == false);
+        rmdir(dir);
+    }
+
+    #[test]
+    fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
+        let file = &Path::new("./tmp/fileinfo_check_exists_b_and_a.txt");
+        File::create(file).write(bytes!("foo"));
+        assert!(file.exists());
+        unlink(file);
+        assert!(!file.exists());
+    }
+
+    #[test]
+    fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
+        let dir = &Path::new("./tmp/before_and_after_dir");
+        assert!(!dir.exists());
+        mkdir(dir, io::UserRWX);
+        assert!(dir.exists());
+        assert!(dir.is_dir());
+        rmdir(dir);
+        assert!(!dir.exists());
+    }
+
+    #[test]
+    fn file_test_directoryinfo_readdir() {
+        use std::str;
+        let dir = &Path::new("./tmp/di_readdir");
+        mkdir(dir, io::UserRWX);
+        let prefix = "foo";
+        for n in range(0,3) {
+            let f = dir.join(format!("{}.txt", n));
+            let mut w = File::create(&f);
+            let msg_str = (prefix + n.to_str().to_owned()).to_owned();
+            let msg = msg_str.as_bytes();
+            w.write(msg);
+        }
+        let files = readdir(dir);
+        let mut mem = [0u8, .. 4];
+        for f in files.iter() {
+            {
+                let n = f.filestem_str();
+                File::open(f).read(mem);
+                let read_str = str::from_utf8(mem);
+                let expected = match n {
+                    None|Some("") => fail!("really shouldn't happen.."),
+                    Some(n) => prefix+n
+                };
+                assert!(expected == read_str);
+            }
+            unlink(f);
+        }
+        rmdir(dir);
+    }
+
+    #[test]
+    fn recursive_mkdir_slash() {
+        mkdir_recursive(&Path::new("/"), io::UserRWX);
+    }
+
+    #[test]
+    fn unicode_path_is_dir() {
+        assert!(Path::new(".").is_dir());
+        assert!(!Path::new("test/stdtest/fs.rs").is_dir());
+
+        let tmpdir = tmpdir();
+
+        let mut dirpath = tmpdir.clone();
+        dirpath.push(format!("test-가一ー你好"));
+        mkdir(&dirpath, io::UserRWX);
+        assert!(dirpath.is_dir());
+
+        let mut filepath = dirpath;
+        filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs");
+        File::create(&filepath); // ignore return; touch only
+        assert!(!filepath.is_dir());
+        assert!(filepath.exists());
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn unicode_path_exists() {
+        assert!(Path::new(".").exists());
+        assert!(!Path::new("test/nonexistent-bogus-path").exists());
+
+        let tmpdir = tmpdir();
+        let unicode = tmpdir.clone();
+        let unicode = unicode.join(format!("test-각丁ー再见"));
+        mkdir(&unicode, io::UserRWX);
+        assert!(unicode.exists());
+        assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn copy_file_does_not_exist() {
+        let from = Path::new("test/nonexistent-bogus-path");
+        let to = Path::new("test/other-bogus-path");
+        match io::result(|| copy(&from, &to)) {
+            Ok(*) => fail!(),
+            Err(*) => {
+                assert!(!from.exists());
+                assert!(!to.exists());
+            }
+        }
+    }
+
+    #[test]
+    fn copy_file_ok() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        File::create(&input).write(bytes!("hello"));
+        copy(&input, &out);
+        let contents = File::open(&out).read_to_end();
+        assert_eq!(contents.as_slice(), bytes!("hello"));
+
+        assert_eq!(input.stat().perm, out.stat().perm);
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn copy_file_dst_dir() {
+        let tmpdir = tmpdir();
+        let out = tmpdir.join("out");
+
+        File::create(&out);
+        match io::result(|| copy(&out, &tmpdir)) {
+            Ok(*) => fail!(), Err(*) => {}
+        }
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn copy_file_dst_exists() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in");
+        let output = tmpdir.join("out");
+
+        File::create(&input).write("foo".as_bytes());
+        File::create(&output).write("bar".as_bytes());
+        copy(&input, &output);
+
+        assert_eq!(File::open(&output).read_to_end(),
+                   (bytes!("foo")).to_owned());
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn copy_file_src_dir() {
+        let tmpdir = tmpdir();
+        let out = tmpdir.join("out");
+
+        match io::result(|| copy(&tmpdir, &out)) {
+            Ok(*) => fail!(), Err(*) => {}
+        }
+        assert!(!out.exists());
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn copy_file_preserves_perm_bits() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        File::create(&input);
+        chmod(&input, io::UserRead);
+        copy(&input, &out);
+        assert!(out.stat().perm & io::UserWrite == 0);
+
+        chmod(&input, io::UserFile);
+        chmod(&out, io::UserFile);
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    #[ignore(cfg(windows))] // FIXME(#10264) operation not permitted?
+    fn symlinks_work() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        File::create(&input).write("foobar".as_bytes());
+        symlink(&input, &out);
+        assert_eq!(lstat(&out).kind, io::TypeSymlink);
+        assert_eq!(stat(&out).size, stat(&input).size);
+        assert_eq!(File::open(&out).read_to_end(), (bytes!("foobar")).to_owned());
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    #[ignore(cfg(windows))] // apparently windows doesn't like symlinks
+    fn symlink_noexist() {
+        let tmpdir = tmpdir();
+        // symlinks can point to things that don't exist
+        symlink(&tmpdir.join("foo"), &tmpdir.join("bar"));
+        assert!(readlink(&tmpdir.join("bar")).unwrap() == tmpdir.join("foo"));
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn readlink_not_symlink() {
+        let tmpdir = tmpdir();
+        match io::result(|| readlink(&tmpdir)) {
+            Ok(*) => fail!("wanted a failure"),
+            Err(*) => {}
+        }
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn links_work() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        File::create(&input).write("foobar".as_bytes());
+        link(&input, &out);
+        assert_eq!(lstat(&out).kind, io::TypeFile);
+        assert_eq!(stat(&out).size, stat(&input).size);
+        assert_eq!(stat(&out).unstable.nlink, 2);
+        assert_eq!(File::open(&out).read_to_end(), (bytes!("foobar")).to_owned());
+
+        // can't link to yourself
+        match io::result(|| link(&input, &input)) {
+            Ok(*) => fail!("wanted a failure"),
+            Err(*) => {}
+        }
+        // can't link to something that doesn't exist
+        match io::result(|| link(&tmpdir.join("foo"), &tmpdir.join("bar"))) {
+            Ok(*) => fail!("wanted a failure"),
+            Err(*) => {}
+        }
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn chmod_works() {
+        let tmpdir = tmpdir();
+        let file = tmpdir.join("in.txt");
+
+        File::create(&file);
+        assert!(stat(&file).perm & io::UserWrite == io::UserWrite);
+        chmod(&file, io::UserRead);
+        assert!(stat(&file).perm & io::UserWrite == 0);
+
+        match io::result(|| chmod(&tmpdir.join("foo"), io::UserRWX)) {
+            Ok(*) => fail!("wanted a failure"),
+            Err(*) => {}
+        }
+
+        chmod(&file, io::UserFile);
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn sync_doesnt_kill_anything() {
+        let tmpdir = tmpdir();
+        let path = tmpdir.join("in.txt");
+
+        let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap();
+        file.fsync();
+        file.datasync();
+        file.write(bytes!("foo"));
+        file.fsync();
+        file.datasync();
+        free(file);
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn truncate_works() {
+        let tmpdir = tmpdir();
+        let path = tmpdir.join("in.txt");
+
+        let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap();
+        file.write(bytes!("foo"));
+
+        // Do some simple things with truncation
+        assert_eq!(stat(&path).size, 3);
+        file.truncate(10);
+        assert_eq!(stat(&path).size, 10);
+        file.write(bytes!("bar"));
+        assert_eq!(stat(&path).size, 10);
+        assert_eq!(File::open(&path).read_to_end(),
+                   (bytes!("foobar", 0, 0, 0, 0)).to_owned());
+
+        // Truncate to a smaller length, don't seek, and then write something.
+        // Ensure that the intermediate zeroes are all filled in (we're seeked
+        // past the end of the file).
+        file.truncate(2);
+        assert_eq!(stat(&path).size, 2);
+        file.write(bytes!("wut"));
+        assert_eq!(stat(&path).size, 9);
+        assert_eq!(File::open(&path).read_to_end(),
+                   (bytes!("fo", 0, 0, 0, 0, "wut")).to_owned());
+        free(file);
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn open_flavors() {
+        let tmpdir = tmpdir();
+
+        match io::result(|| File::open_mode(&tmpdir.join("a"), io::Open,
+                                            io::Read)) {
+            Ok(*) => fail!(), Err(*) => {}
+        }
+        File::open_mode(&tmpdir.join("b"), io::Open, io::Write).unwrap();
+        File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite).unwrap();
+        File::open_mode(&tmpdir.join("d"), io::Append, io::Write).unwrap();
+        File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite).unwrap();
+        File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write).unwrap();
+        File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite).unwrap();
+
+        File::create(&tmpdir.join("h")).write("foo".as_bytes());
+        File::open_mode(&tmpdir.join("h"), io::Open, io::Read).unwrap();
+        {
+            let mut f = File::open_mode(&tmpdir.join("h"), io::Open,
+                                        io::Read).unwrap();
+            match io::result(|| f.write("wut".as_bytes())) {
+                Ok(*) => fail!(), Err(*) => {}
+            }
+        }
+        assert_eq!(stat(&tmpdir.join("h")).size, 3);
+        {
+            let mut f = File::open_mode(&tmpdir.join("h"), io::Append,
+                                        io::Write).unwrap();
+            f.write("bar".as_bytes());
+        }
+        assert_eq!(stat(&tmpdir.join("h")).size, 6);
+        {
+            let mut f = File::open_mode(&tmpdir.join("h"), io::Truncate,
+                                        io::Write).unwrap();
+            f.write("bar".as_bytes());
+        }
+        assert_eq!(stat(&tmpdir.join("h")).size, 3);
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn utime() {
+        let tmpdir = tmpdir();
+        let path = tmpdir.join("a");
+        File::create(&path);
+
+        change_file_times(&path, 1000, 2000);
+        assert_eq!(path.stat().accessed, 1000);
+        assert_eq!(path.stat().modified, 2000);
+
+        rmdir_recursive(&tmpdir);
+    }
+
+    #[test]
+    fn utime_noexist() {
+        let tmpdir = tmpdir();
+
+        match io::result(|| change_file_times(&tmpdir.join("a"), 100, 200)) {
+            Ok(*) => fail!(),
+            Err(*) => {}
+        }
+
+        rmdir_recursive(&tmpdir);
+    }
+}
diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs
new file mode 100644
index 00000000000..e754d9be85e
--- /dev/null
+++ b/src/libstd/io/mem.rs
@@ -0,0 +1,308 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Readers and Writers for in-memory buffers
+//!
+//! # XXX
+//!
+//! * Should probably have something like this for strings.
+//! * Should they implement Closable? Would take extra state.
+
+use cmp::min;
+use prelude::*;
+use super::*;
+use vec;
+
+/// Writes to an owned, growable byte vector
+pub struct MemWriter {
+    priv buf: ~[u8],
+    priv pos: uint,
+}
+
+impl MemWriter {
+    pub fn new() -> MemWriter {
+        MemWriter { buf: vec::with_capacity(128), pos: 0 }
+    }
+}
+
+impl Writer for MemWriter {
+    fn write(&mut self, buf: &[u8]) {
+        // Make sure the internal buffer is as least as big as where we
+        // currently are
+        let difference = self.pos as i64 - self.buf.len() as i64;
+        if difference > 0 {
+            self.buf.grow(difference as uint, &0);
+        }
+
+        // Figure out what bytes will be used to overwrite what's currently
+        // there (left), and what will be appended on the end (right)
+        let cap = self.buf.len() - self.pos;
+        let (left, right) = if cap <= buf.len() {
+            (buf.slice_to(cap), buf.slice_from(cap))
+        } else {
+            (buf, &[])
+        };
+
+        // Do the necessary writes
+        if left.len() > 0 {
+            vec::bytes::copy_memory(self.buf.mut_slice_from(self.pos),
+                                    left, left.len());
+        }
+        if right.len() > 0 {
+            self.buf.push_all(right);
+        }
+
+        // Bump us forward
+        self.pos += buf.len();
+    }
+}
+
+impl Seek for MemWriter {
+    fn tell(&self) -> u64 { self.pos as u64 }
+
+    fn seek(&mut self, pos: i64, style: SeekStyle) {
+        match style {
+            SeekSet => { self.pos = pos as uint; }
+            SeekEnd => { self.pos = self.buf.len() + pos as uint; }
+            SeekCur => { self.pos += pos as uint; }
+        }
+    }
+}
+
+impl Decorator<~[u8]> for MemWriter {
+    fn inner(self) -> ~[u8] { self.buf }
+    fn inner_ref<'a>(&'a self) -> &'a ~[u8] { &self.buf }
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] { &mut self.buf }
+}
+
+/// Reads from an owned byte vector
+pub struct MemReader {
+    priv buf: ~[u8],
+    priv pos: uint
+}
+
+impl MemReader {
+    pub fn new(buf: ~[u8]) -> MemReader {
+        MemReader {
+            buf: buf,
+            pos: 0
+        }
+    }
+}
+
+impl Reader for MemReader {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        { if self.eof() { return None; } }
+
+        let write_len = min(buf.len(), self.buf.len() - self.pos);
+        {
+            let input = self.buf.slice(self.pos, self.pos + write_len);
+            let output = buf.mut_slice(0, write_len);
+            assert_eq!(input.len(), output.len());
+            vec::bytes::copy_memory(output, input, write_len);
+        }
+        self.pos += write_len;
+        assert!(self.pos <= self.buf.len());
+
+        return Some(write_len);
+    }
+
+    fn eof(&mut self) -> bool { self.pos == self.buf.len() }
+}
+
+impl Seek for MemReader {
+    fn tell(&self) -> u64 { self.pos as u64 }
+
+    fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() }
+}
+
+impl Decorator<~[u8]> for MemReader {
+
+    fn inner(self) -> ~[u8] {
+        match self {
+            MemReader { buf: buf, _ } => buf
+        }
+    }
+
+    fn inner_ref<'a>(&'a self) -> &'a ~[u8] {
+        match *self {
+            MemReader { buf: ref buf, _ } => buf
+        }
+    }
+
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] {
+        match *self {
+            MemReader { buf: ref mut buf, _ } => buf
+        }
+    }
+}
+
+
+/// Writes to a fixed-size byte slice
+pub struct BufWriter<'self> {
+    priv buf: &'self mut [u8],
+    priv pos: uint
+}
+
+impl<'self> BufWriter<'self> {
+    pub fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> {
+        BufWriter {
+            buf: buf,
+            pos: 0
+        }
+    }
+}
+
+impl<'self> Writer for BufWriter<'self> {
+    fn write(&mut self, _buf: &[u8]) { fail!() }
+
+    fn flush(&mut self) { fail!() }
+}
+
+impl<'self> Seek for BufWriter<'self> {
+    fn tell(&self) -> u64 { fail!() }
+
+    fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() }
+}
+
+
+/// Reads from a fixed-size byte slice
+pub struct BufReader<'self> {
+    priv buf: &'self [u8],
+    priv pos: uint
+}
+
+impl<'self> BufReader<'self> {
+    pub fn new<'a>(buf: &'a [u8]) -> BufReader<'a> {
+        BufReader {
+            buf: buf,
+            pos: 0
+        }
+    }
+}
+
+impl<'self> Reader for BufReader<'self> {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        { if self.eof() { return None; } }
+
+        let write_len = min(buf.len(), self.buf.len() - self.pos);
+        {
+            let input = self.buf.slice(self.pos, self.pos + write_len);
+            let output = buf.mut_slice(0, write_len);
+            assert_eq!(input.len(), output.len());
+            vec::bytes::copy_memory(output, input, write_len);
+        }
+        self.pos += write_len;
+        assert!(self.pos <= self.buf.len());
+
+        return Some(write_len);
+     }
+
+    fn eof(&mut self) -> bool { self.pos == self.buf.len() }
+}
+
+impl<'self> Seek for BufReader<'self> {
+    fn tell(&self) -> u64 { self.pos as u64 }
+
+    fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() }
+}
+
+///Calls a function with a MemWriter and returns
+///the writer's stored vector.
+pub fn with_mem_writer(writeFn:&fn(&mut MemWriter)) -> ~[u8] {
+    let mut writer = MemWriter::new();
+    writeFn(&mut writer);
+    writer.inner()
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::*;
+    use super::*;
+    use io::*;
+
+    #[test]
+    fn test_mem_writer() {
+        let mut writer = MemWriter::new();
+        assert_eq!(writer.tell(), 0);
+        writer.write([0]);
+        assert_eq!(writer.tell(), 1);
+        writer.write([1, 2, 3]);
+        writer.write([4, 5, 6, 7]);
+        assert_eq!(writer.tell(), 8);
+        assert_eq!(*writer.inner_ref(), ~[0, 1, 2, 3, 4, 5, 6, 7]);
+
+        writer.seek(0, SeekSet);
+        assert_eq!(writer.tell(), 0);
+        writer.write([3, 4]);
+        assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 3, 4, 5, 6, 7]);
+
+        writer.seek(1, SeekCur);
+        writer.write([0, 1]);
+        assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 0, 1, 5, 6, 7]);
+
+        writer.seek(-1, SeekEnd);
+        writer.write([1, 2]);
+        assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 0, 1, 5, 6, 1, 2]);
+
+        writer.seek(1, SeekEnd);
+        writer.write([1]);
+        assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]);
+    }
+
+    #[test]
+    fn test_mem_reader() {
+        let mut reader = MemReader::new(~[0, 1, 2, 3, 4, 5, 6, 7]);
+        let mut buf = [];
+        assert_eq!(reader.read(buf), Some(0));
+        assert_eq!(reader.tell(), 0);
+        let mut buf = [0];
+        assert_eq!(reader.read(buf), Some(1));
+        assert_eq!(reader.tell(), 1);
+        assert_eq!(buf, [0]);
+        let mut buf = [0, ..4];
+        assert_eq!(reader.read(buf), Some(4));
+        assert_eq!(reader.tell(), 5);
+        assert_eq!(buf, [1, 2, 3, 4]);
+        assert_eq!(reader.read(buf), Some(3));
+        assert_eq!(buf.slice(0, 3), [5, 6, 7]);
+        assert!(reader.eof());
+        assert_eq!(reader.read(buf), None);
+        assert!(reader.eof());
+    }
+
+    #[test]
+    fn test_buf_reader() {
+        let in_buf = ~[0, 1, 2, 3, 4, 5, 6, 7];
+        let mut reader = BufReader::new(in_buf);
+        let mut buf = [];
+        assert_eq!(reader.read(buf), Some(0));
+        assert_eq!(reader.tell(), 0);
+        let mut buf = [0];
+        assert_eq!(reader.read(buf), Some(1));
+        assert_eq!(reader.tell(), 1);
+        assert_eq!(buf, [0]);
+        let mut buf = [0, ..4];
+        assert_eq!(reader.read(buf), Some(4));
+        assert_eq!(reader.tell(), 5);
+        assert_eq!(buf, [1, 2, 3, 4]);
+        assert_eq!(reader.read(buf), Some(3));
+        assert_eq!(buf.slice(0, 3), [5, 6, 7]);
+        assert!(reader.eof());
+        assert_eq!(reader.read(buf), None);
+        assert!(reader.eof());
+    }
+
+    #[test]
+    fn test_with_mem_writer() {
+        let buf = with_mem_writer(|wr| wr.write([1,2,3,4,5,6,7]));
+        assert_eq!(buf, ~[1,2,3,4,5,6,7]);
+    }
+}
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
new file mode 100644
index 00000000000..8858d0a14ad
--- /dev/null
+++ b/src/libstd/io/mod.rs
@@ -0,0 +1,1226 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*! Synchronous I/O
+
+This module defines the Rust interface for synchronous I/O.
+It models byte-oriented input and output with the Reader and Writer traits.
+Types that implement both `Reader` and `Writer` are called 'streams',
+and automatically implement the `Stream` trait.
+Implementations are provided for common I/O streams like
+file, TCP, UDP, Unix domain sockets.
+Readers and Writers may be composed to add capabilities like string
+parsing, encoding, and compression.
+
+# Examples
+
+Some examples of obvious things you might want to do
+
+* Read lines from stdin
+
+    for stdin().each_line |line| {
+        println(line)
+    }
+
+* Read a complete file to a string, (converting newlines?)
+
+    let contents = File::open("message.txt").read_to_str(); // read_to_str??
+
+* Write a line to a file
+
+    let file = File::open("message.txt", Create, Write);
+    file.write_line("hello, file!");
+
+* Iterate over the lines of a file
+
+    do File::open("message.txt").each_line |line| {
+        println(line)
+    }
+
+* Pull the lines of a file into a vector of strings
+
+    let lines = File::open("message.txt").line_iter().to_vec();
+
+* Make an simple HTTP request
+
+    let socket = TcpStream::open("localhost:8080");
+    socket.write_line("GET / HTTP/1.0");
+    socket.write_line("");
+    let response = socket.read_to_end();
+
+* Connect based on URL? Requires thinking about where the URL type lives
+  and how to make protocol handlers extensible, e.g. the "tcp" protocol
+  yields a `TcpStream`.
+
+    connect("tcp://localhost:8080");
+
+# Terms
+
+* Reader - An I/O source, reads bytes into a buffer
+* Writer - An I/O sink, writes bytes from a buffer
+* Stream - Typical I/O sources like files and sockets are both Readers and Writers,
+  and are collectively referred to a `streams`.
+* Decorator - A Reader or Writer that composes with others to add additional capabilities
+  such as encoding or decoding
+
+# Blocking and synchrony
+
+When discussing I/O you often hear the terms 'synchronous' and
+'asynchronous', along with 'blocking' and 'non-blocking' compared and
+contrasted. A synchronous I/O interface performs each I/O operation to
+completion before proceeding to the next. Synchronous interfaces are
+usually used in imperative style as a sequence of commands. An
+asynchronous interface allows multiple I/O requests to be issued
+simultaneously, without waiting for each to complete before proceeding
+to the next.
+
+Asynchronous interfaces are used to achieve 'non-blocking' I/O. In
+traditional single-threaded systems, performing a synchronous I/O
+operation means that the program stops all activity (it 'blocks')
+until the I/O is complete. Blocking is bad for performance when
+there are other computations that could be done.
+
+Asynchronous interfaces are most often associated with the callback
+(continuation-passing) style popularised by node.js. Such systems rely
+on all computations being run inside an event loop which maintains a
+list of all pending I/O events; when one completes the registered
+callback is run and the code that made the I/O request continues.
+Such interfaces achieve non-blocking at the expense of being more
+difficult to reason about.
+
+Rust's I/O interface is synchronous - easy to read - and non-blocking by default.
+
+Remember that Rust tasks are 'green threads', lightweight threads that
+are multiplexed onto a single operating system thread. If that system
+thread blocks then no other task may proceed. Rust tasks are
+relatively cheap to create, so as long as other tasks are free to
+execute then non-blocking code may be written by simply creating a new
+task.
+
+When discussing blocking in regards to Rust's I/O model, we are
+concerned with whether performing I/O blocks other Rust tasks from
+proceeding. In other words, when a task calls `read`, it must then
+wait (or 'sleep', or 'block') until the call to `read` is complete.
+During this time, other tasks may or may not be executed, depending on
+how `read` is implemented.
+
+
+Rust's default I/O implementation is non-blocking; by cooperating
+directly with the task scheduler it arranges to never block progress
+of *other* tasks. Under the hood, Rust uses asynchronous I/O via a
+per-scheduler (and hence per-thread) event loop. Synchronous I/O
+requests are implemented by descheduling the running task and
+performing an asynchronous request; the task is only resumed once the
+asynchronous request completes.
+
+For blocking (but possibly more efficient) implementations, look
+in the `io::native` module.
+
+# Error Handling
+
+I/O is an area where nearly every operation can result in unexpected
+errors. It should allow errors to be handled efficiently.
+It needs to be convenient to use I/O when you don't care
+about dealing with specific errors.
+
+Rust's I/O employs a combination of techniques to reduce boilerplate
+while still providing feedback about errors. The basic strategy:
+
+* Errors are fatal by default, resulting in task failure
+* Errors raise the `io_error` condition which provides an opportunity to inspect
+  an IoError object containing details.
+* Return values must have a sensible null or zero value which is returned
+  if a condition is handled successfully. This may be an `Option`, an empty
+  vector, or other designated error value.
+* Common traits are implemented for `Option`, e.g. `impl<R: Reader> Reader for Option<R>`,
+  so that nullable values do not have to be 'unwrapped' before use.
+
+These features combine in the API to allow for expressions like
+`File::new("diary.txt").write_line("met a girl")` without having to
+worry about whether "diary.txt" exists or whether the write
+succeeds. As written, if either `new` or `write_line` encounters
+an error the task will fail.
+
+If you wanted to handle the error though you might write
+
+    let mut error = None;
+    do io_error::cond(|e: IoError| {
+        error = Some(e);
+    }).in {
+        File::new("diary.txt").write_line("met a girl");
+    }
+
+    if error.is_some() {
+        println("failed to write my diary");
+    }
+
+XXX: Need better condition handling syntax
+
+In this case the condition handler will have the opportunity to
+inspect the IoError raised by either the call to `new` or the call to
+`write_line`, but then execution will continue.
+
+So what actually happens if `new` encounters an error? To understand
+that it's important to know that what `new` returns is not a `File`
+but an `Option<File>`.  If the file does not open, and the condition
+is handled, then `new` will simply return `None`. Because there is an
+implementation of `Writer` (the trait required ultimately required for
+types to implement `write_line`) there is no need to inspect or unwrap
+the `Option<File>` and we simply call `write_line` on it.  If `new`
+returned a `None` then the followup call to `write_line` will also
+raise an error.
+
+## Concerns about this strategy
+
+This structure will encourage a programming style that is prone
+to errors similar to null pointer dereferences.
+In particular code written to ignore errors and expect conditions to be unhandled
+will start passing around null or zero objects when wrapped in a condition handler.
+
+* XXX: How should we use condition handlers that return values?
+* XXX: Should EOF raise default conditions when EOF is not an error?
+
+# Issues with i/o scheduler affinity, work stealing, task pinning
+
+# Resource management
+
+* `close` vs. RAII
+
+# Paths, URLs and overloaded constructors
+
+
+
+# Scope
+
+In scope for core
+
+* Url?
+
+Some I/O things don't belong in core
+
+  - url
+  - net - `fn connect`
+    - http
+  - flate
+
+Out of scope
+
+* Async I/O. We'll probably want it eventually
+
+
+# XXX Questions and issues
+
+* Should default constructors take `Path` or `&str`? `Path` makes simple cases verbose.
+  Overloading would be nice.
+* Add overloading for Path and &str and Url &str
+* stdin/err/out
+* print, println, etc.
+* fsync
+* relationship with filesystem querying, Directory, File types etc.
+* Rename Reader/Writer to ByteReader/Writer, make Reader/Writer generic?
+* Can Port and Chan be implementations of a generic Reader<T>/Writer<T>?
+* Trait for things that are both readers and writers, Stream?
+* How to handle newline conversion
+* String conversion
+* open vs. connect for generic stream opening
+* Do we need `close` at all? dtors might be good enough
+* How does I/O relate to the Iterator trait?
+* std::base64 filters
+* Using conditions is a big unknown since we don't have much experience with them
+* Too many uses of OtherIoError
+
+*/
+
+#[allow(missing_doc)];
+
+use cast;
+use container::Container;
+use int;
+use iter::Iterator;
+use option::{Option, Some, None};
+use path::Path;
+use result::{Ok, Err, Result};
+use str::{StrSlice, OwnedStr};
+use to_str::ToStr;
+use uint;
+use unstable::finally::Finally;
+use vec::{OwnedVector, MutableVector};
+use vec;
+
+// Reexports
+pub use self::stdio::stdin;
+pub use self::stdio::stdout;
+pub use self::stdio::stderr;
+pub use self::stdio::print;
+pub use self::stdio::println;
+
+pub use self::fs::File;
+pub use self::timer::Timer;
+pub use self::net::ip::IpAddr;
+pub use self::net::tcp::TcpListener;
+pub use self::net::tcp::TcpStream;
+pub use self::net::udp::UdpStream;
+pub use self::pipe::PipeStream;
+pub use self::process::Process;
+
+/// Synchronous, non-blocking filesystem operations.
+pub mod fs;
+
+/// Synchronous, in-memory I/O.
+pub mod pipe;
+
+/// Child process management.
+pub mod process;
+
+/// Synchronous, non-blocking network I/O.
+pub mod net;
+
+/// Readers and Writers for memory buffers and strings.
+pub mod mem;
+
+/// Non-blocking access to stdin, stdout, stderr
+pub mod stdio;
+
+/// Implementations for Option
+mod option;
+
+/// Basic stream compression. XXX: Belongs with other flate code
+pub mod flate;
+
+/// Interop between byte streams and pipes. Not sure where it belongs
+pub mod comm_adapters;
+
+/// Extension traits
+pub mod extensions;
+
+/// Basic Timer
+pub mod timer;
+
+/// Buffered I/O wrappers
+pub mod buffered;
+
+/// Thread-blocking implementations
+pub mod native {
+    /// Posix file I/O
+    pub mod file;
+    /// Process spawning and child management
+    pub mod process;
+    /// Posix stdio
+    pub mod stdio;
+
+    /// Sockets
+    /// # XXX - implement this
+    pub mod net {
+        pub mod tcp { }
+        pub mod udp { }
+        #[cfg(unix)]
+        pub mod unix { }
+    }
+}
+
+/// Signal handling
+pub mod signal;
+
+/// The default buffer size for various I/O operations
+static DEFAULT_BUF_SIZE: uint = 1024 * 64;
+
+/// The type passed to I/O condition handlers to indicate error
+///
+/// # XXX
+///
+/// Is something like this sufficient? It's kind of archaic
+pub struct IoError {
+    kind: IoErrorKind,
+    desc: &'static str,
+    detail: Option<~str>
+}
+
+// FIXME: #8242 implementing manually because deriving doesn't work for some reason
+impl ToStr for IoError {
+    fn to_str(&self) -> ~str {
+        let mut s = ~"IoError { kind: ";
+        s.push_str(self.kind.to_str());
+        s.push_str(", desc: ");
+        s.push_str(self.desc);
+        s.push_str(", detail: ");
+        s.push_str(self.detail.to_str());
+        s.push_str(" }");
+        s
+    }
+}
+
+#[deriving(Eq)]
+pub enum IoErrorKind {
+    PreviousIoError,
+    OtherIoError,
+    EndOfFile,
+    FileNotFound,
+    PermissionDenied,
+    ConnectionFailed,
+    Closed,
+    ConnectionRefused,
+    ConnectionReset,
+    ConnectionAborted,
+    NotConnected,
+    BrokenPipe,
+    PathAlreadyExists,
+    PathDoesntExist,
+    MismatchedFileTypeForOperation,
+    ResourceUnavailable,
+    IoUnavailable,
+}
+
+// FIXME: #8242 implementing manually because deriving doesn't work for some reason
+impl ToStr for IoErrorKind {
+    fn to_str(&self) -> ~str {
+        match *self {
+            PreviousIoError => ~"PreviousIoError",
+            OtherIoError => ~"OtherIoError",
+            EndOfFile => ~"EndOfFile",
+            FileNotFound => ~"FileNotFound",
+            PermissionDenied => ~"PermissionDenied",
+            ConnectionFailed => ~"ConnectionFailed",
+            Closed => ~"Closed",
+            ConnectionRefused => ~"ConnectionRefused",
+            ConnectionReset => ~"ConnectionReset",
+            NotConnected => ~"NotConnected",
+            BrokenPipe => ~"BrokenPipe",
+            PathAlreadyExists => ~"PathAlreadyExists",
+            PathDoesntExist => ~"PathDoesntExist",
+            MismatchedFileTypeForOperation => ~"MismatchedFileTypeForOperation",
+            IoUnavailable => ~"IoUnavailable",
+            ResourceUnavailable => ~"ResourceUnavailable",
+            ConnectionAborted => ~"ConnectionAborted",
+        }
+    }
+}
+
+// XXX: Can't put doc comments on macros
+// Raised by `I/O` operations on error.
+condition! {
+    pub io_error: IoError -> ();
+}
+
+/// Helper for wrapper calls where you want to
+/// ignore any io_errors that might be raised
+pub fn ignore_io_error<T>(cb: &fn() -> T) -> T {
+    do io_error::cond.trap(|_| {
+        // just swallow the error.. downstream users
+        // who can make a decision based on a None result
+        // won't care
+    }).inside {
+        cb()
+    }
+}
+
+/// Helper for catching an I/O error and wrapping it in a Result object. The
+/// return result will be the last I/O error that happened or the result of the
+/// closure if no error occurred.
+pub fn result<T>(cb: &fn() -> T) -> Result<T, IoError> {
+    let mut err = None;
+    let ret = io_error::cond.trap(|e| {
+        if err.is_none() {
+            err = Some(e);
+        }
+    }).inside(cb);
+    match err {
+        Some(e) => Err(e),
+        None => Ok(ret),
+    }
+}
+
+pub trait Reader {
+
+    // Only two methods which need to get implemented for this trait
+
+    /// Read bytes, up to the length of `buf` and place them in `buf`.
+    /// Returns the number of bytes read. The number of bytes read my
+    /// be less than the number requested, even 0. Returns `None` on EOF.
+    ///
+    /// # Failure
+    ///
+    /// Raises the `io_error` condition on error. If the condition
+    /// is handled then no guarantee is made about the number of bytes
+    /// read and the contents of `buf`. If the condition is handled
+    /// returns `None` (XXX see below).
+    ///
+    /// # XXX
+    ///
+    /// * Should raise_default error on eof?
+    /// * If the condition is handled it should still return the bytes read,
+    ///   in which case there's no need to return Option - but then you *have*
+    ///   to install a handler to detect eof.
+    ///
+    /// This doesn't take a `len` argument like the old `read`.
+    /// Will people often need to slice their vectors to call this
+    /// and will that be annoying?
+    /// Is it actually possible for 0 bytes to be read successfully?
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint>;
+
+    /// Return whether the Reader has reached the end of the stream.
+    ///
+    /// # Example
+    ///
+    ///     let reader = File::open(&Path::new("foo.txt"))
+    ///     while !reader.eof() {
+    ///         println(reader.read_line());
+    ///     }
+    ///
+    /// # Failure
+    ///
+    /// Returns `true` on failure.
+    fn eof(&mut self) -> bool;
+
+    // Convenient helper methods based on the above methods
+
+    /// Reads a single byte. Returns `None` on EOF.
+    ///
+    /// # Failure
+    ///
+    /// Raises the same conditions as the `read` method. Returns
+    /// `None` if the condition is handled.
+    fn read_byte(&mut self) -> Option<u8> {
+        let mut buf = [0];
+        match self.read(buf) {
+            Some(0) => {
+                debug!("read 0 bytes. trying again");
+                self.read_byte()
+            }
+            Some(1) => Some(buf[0]),
+            Some(_) => unreachable!(),
+            None => None
+        }
+    }
+
+    /// Reads `len` bytes and appends them to a vector.
+    ///
+    /// May push fewer than the requested number of bytes on error
+    /// or EOF. Returns true on success, false on EOF or error.
+    ///
+    /// # Failure
+    ///
+    /// Raises the same conditions as `read`. Additionally raises `io_error`
+    /// on EOF. If `io_error` is handled then `push_bytes` may push less
+    /// than the requested number of bytes.
+    fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) {
+        unsafe {
+            let start_len = buf.len();
+            let mut total_read = 0;
+
+            buf.reserve_additional(len);
+            vec::raw::set_len(buf, start_len + len);
+
+            do (|| {
+                while total_read < len {
+                    let len = buf.len();
+                    let slice = buf.mut_slice(start_len + total_read, len);
+                    match self.read(slice) {
+                        Some(nread) => {
+                            total_read += nread;
+                        }
+                        None => {
+                            io_error::cond.raise(standard_error(EndOfFile));
+                            break;
+                        }
+                    }
+                }
+            }).finally {
+                vec::raw::set_len(buf, start_len + total_read);
+            }
+        }
+    }
+
+    /// Reads `len` bytes and gives you back a new vector of length `len`
+    ///
+    /// # Failure
+    ///
+    /// Raises the same conditions as `read`. Additionally raises `io_error`
+    /// on EOF. If `io_error` is handled then the returned vector may
+    /// contain less than the requested number of bytes.
+    fn read_bytes(&mut self, len: uint) -> ~[u8] {
+        let mut buf = vec::with_capacity(len);
+        self.push_bytes(&mut buf, len);
+        return buf;
+    }
+
+    /// Reads all remaining bytes from the stream.
+    ///
+    /// # Failure
+    ///
+    /// Raises the same conditions as the `read` method.
+    fn read_to_end(&mut self) -> ~[u8] {
+        let mut buf = vec::with_capacity(DEFAULT_BUF_SIZE);
+        let mut keep_reading = true;
+        do io_error::cond.trap(|e| {
+            if e.kind == EndOfFile {
+                keep_reading = false;
+            } else {
+                io_error::cond.raise(e)
+            }
+        }).inside {
+            while keep_reading {
+                self.push_bytes(&mut buf, DEFAULT_BUF_SIZE)
+            }
+        }
+        return buf;
+    }
+
+    /// Create an iterator that reads a single byte on
+    /// each iteration, until EOF.
+    ///
+    /// # Failure
+    ///
+    /// Raises the same conditions as the `read` method, for
+    /// each call to its `.next()` method.
+    /// Ends the iteration if the condition is handled.
+    fn bytes(self) -> extensions::ByteIterator<Self> {
+        extensions::ByteIterator::new(self)
+    }
+
+    // Byte conversion helpers
+
+    /// Reads `n` little-endian unsigned integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_le_uint_n(&mut self, nbytes: uint) -> u64 {
+        assert!(nbytes > 0 && nbytes <= 8);
+
+        let mut val = 0u64;
+        let mut pos = 0;
+        let mut i = nbytes;
+        while i > 0 {
+            val += (self.read_u8() as u64) << pos;
+            pos += 8;
+            i -= 1;
+        }
+        val
+    }
+
+    /// Reads `n` little-endian signed integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_le_int_n(&mut self, nbytes: uint) -> i64 {
+        extend_sign(self.read_le_uint_n(nbytes), nbytes)
+    }
+
+    /// Reads `n` big-endian unsigned integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_be_uint_n(&mut self, nbytes: uint) -> u64 {
+        assert!(nbytes > 0 && nbytes <= 8);
+
+        let mut val = 0u64;
+        let mut i = nbytes;
+        while i > 0 {
+            i -= 1;
+            val += (self.read_u8() as u64) << i * 8;
+        }
+        val
+    }
+
+    /// Reads `n` big-endian signed integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_be_int_n(&mut self, nbytes: uint) -> i64 {
+        extend_sign(self.read_be_uint_n(nbytes), nbytes)
+    }
+
+    /// Reads a little-endian unsigned integer.
+    ///
+    /// The number of bytes returned is system-dependant.
+    fn read_le_uint(&mut self) -> uint {
+        self.read_le_uint_n(uint::bytes) as uint
+    }
+
+    /// Reads a little-endian integer.
+    ///
+    /// The number of bytes returned is system-dependant.
+    fn read_le_int(&mut self) -> int {
+        self.read_le_int_n(int::bytes) as int
+    }
+
+    /// Reads a big-endian unsigned integer.
+    ///
+    /// The number of bytes returned is system-dependant.
+    fn read_be_uint(&mut self) -> uint {
+        self.read_be_uint_n(uint::bytes) as uint
+    }
+
+    /// Reads a big-endian integer.
+    ///
+    /// The number of bytes returned is system-dependant.
+    fn read_be_int(&mut self) -> int {
+        self.read_be_int_n(int::bytes) as int
+    }
+
+    /// Reads a big-endian `u64`.
+    ///
+    /// `u64`s are 8 bytes long.
+    fn read_be_u64(&mut self) -> u64 {
+        self.read_be_uint_n(8) as u64
+    }
+
+    /// Reads a big-endian `u32`.
+    ///
+    /// `u32`s are 4 bytes long.
+    fn read_be_u32(&mut self) -> u32 {
+        self.read_be_uint_n(4) as u32
+    }
+
+    /// Reads a big-endian `u16`.
+    ///
+    /// `u16`s are 2 bytes long.
+    fn read_be_u16(&mut self) -> u16 {
+        self.read_be_uint_n(2) as u16
+    }
+
+    /// Reads a big-endian `i64`.
+    ///
+    /// `i64`s are 8 bytes long.
+    fn read_be_i64(&mut self) -> i64 {
+        self.read_be_int_n(8) as i64
+    }
+
+    /// Reads a big-endian `i32`.
+    ///
+    /// `i32`s are 4 bytes long.
+    fn read_be_i32(&mut self) -> i32 {
+        self.read_be_int_n(4) as i32
+    }
+
+    /// Reads a big-endian `i16`.
+    ///
+    /// `i16`s are 2 bytes long.
+    fn read_be_i16(&mut self) -> i16 {
+        self.read_be_int_n(2) as i16
+    }
+
+    /// Reads a big-endian `f64`.
+    ///
+    /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers.
+    fn read_be_f64(&mut self) -> f64 {
+        unsafe {
+            cast::transmute::<u64, f64>(self.read_be_u64())
+        }
+    }
+
+    /// Reads a big-endian `f32`.
+    ///
+    /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers.
+    fn read_be_f32(&mut self) -> f32 {
+        unsafe {
+            cast::transmute::<u32, f32>(self.read_be_u32())
+        }
+    }
+
+    /// Reads a little-endian `u64`.
+    ///
+    /// `u64`s are 8 bytes long.
+    fn read_le_u64(&mut self) -> u64 {
+        self.read_le_uint_n(8) as u64
+    }
+
+    /// Reads a little-endian `u32`.
+    ///
+    /// `u32`s are 4 bytes long.
+    fn read_le_u32(&mut self) -> u32 {
+        self.read_le_uint_n(4) as u32
+    }
+
+    /// Reads a little-endian `u16`.
+    ///
+    /// `u16`s are 2 bytes long.
+    fn read_le_u16(&mut self) -> u16 {
+        self.read_le_uint_n(2) as u16
+    }
+
+    /// Reads a little-endian `i64`.
+    ///
+    /// `i64`s are 8 bytes long.
+    fn read_le_i64(&mut self) -> i64 {
+        self.read_le_int_n(8) as i64
+    }
+
+    /// Reads a little-endian `i32`.
+    ///
+    /// `i32`s are 4 bytes long.
+    fn read_le_i32(&mut self) -> i32 {
+        self.read_le_int_n(4) as i32
+    }
+
+    /// Reads a little-endian `i16`.
+    ///
+    /// `i16`s are 2 bytes long.
+    fn read_le_i16(&mut self) -> i16 {
+        self.read_le_int_n(2) as i16
+    }
+
+    /// Reads a little-endian `f64`.
+    ///
+    /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers.
+    fn read_le_f64(&mut self) -> f64 {
+        unsafe {
+            cast::transmute::<u64, f64>(self.read_le_u64())
+        }
+    }
+
+    /// Reads a little-endian `f32`.
+    ///
+    /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers.
+    fn read_le_f32(&mut self) -> f32 {
+        unsafe {
+            cast::transmute::<u32, f32>(self.read_le_u32())
+        }
+    }
+
+    /// Read a u8.
+    ///
+    /// `u8`s are 1 byte.
+    fn read_u8(&mut self) -> u8 {
+        match self.read_byte() {
+            Some(b) => b as u8,
+            None => 0
+        }
+    }
+
+    /// Read an i8.
+    ///
+    /// `i8`s are 1 byte.
+    fn read_i8(&mut self) -> i8 {
+        match self.read_byte() {
+            Some(b) => b as i8,
+            None => 0
+        }
+    }
+
+}
+
+impl Reader for ~Reader {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.read(buf) }
+    fn eof(&mut self) -> bool { self.eof() }
+}
+
+impl<'self> Reader for &'self mut Reader {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.read(buf) }
+    fn eof(&mut self) -> bool { self.eof() }
+}
+
+fn extend_sign(val: u64, nbytes: uint) -> i64 {
+    let shift = (8 - nbytes) * 8;
+    (val << shift) as i64 >> shift
+}
+
+pub trait Writer {
+    /// Write the given buffer
+    ///
+    /// # Failure
+    ///
+    /// Raises the `io_error` condition on error
+    fn write(&mut self, buf: &[u8]);
+
+    /// Flush this output stream, ensuring that all intermediately buffered
+    /// contents reach their destination.
+    ///
+    /// This is by default a no-op and implementors of the `Writer` trait should
+    /// decide whether their stream needs to be buffered or not.
+    fn flush(&mut self) {}
+
+    /// Write the result of passing n through `int::to_str_bytes`.
+    fn write_int(&mut self, n: int) {
+        int::to_str_bytes(n, 10u, |bytes| self.write(bytes))
+    }
+
+    /// Write the result of passing n through `uint::to_str_bytes`.
+    fn write_uint(&mut self, n: uint) {
+        uint::to_str_bytes(n, 10u, |bytes| self.write(bytes))
+    }
+
+    /// Write a little-endian uint (number of bytes depends on system).
+    fn write_le_uint(&mut self, n: uint) {
+        extensions::u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v))
+    }
+
+    /// Write a little-endian int (number of bytes depends on system).
+    fn write_le_int(&mut self, n: int) {
+        extensions::u64_to_le_bytes(n as u64, int::bytes, |v| self.write(v))
+    }
+
+    /// Write a big-endian uint (number of bytes depends on system).
+    fn write_be_uint(&mut self, n: uint) {
+        extensions::u64_to_be_bytes(n as u64, uint::bytes, |v| self.write(v))
+    }
+
+    /// Write a big-endian int (number of bytes depends on system).
+    fn write_be_int(&mut self, n: int) {
+        extensions::u64_to_be_bytes(n as u64, int::bytes, |v| self.write(v))
+    }
+
+    /// Write a big-endian u64 (8 bytes).
+    fn write_be_u64(&mut self, n: u64) {
+        extensions::u64_to_be_bytes(n, 8u, |v| self.write(v))
+    }
+
+    /// Write a big-endian u32 (4 bytes).
+    fn write_be_u32(&mut self, n: u32) {
+        extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v))
+    }
+
+    /// Write a big-endian u16 (2 bytes).
+    fn write_be_u16(&mut self, n: u16) {
+        extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v))
+    }
+
+    /// Write a big-endian i64 (8 bytes).
+    fn write_be_i64(&mut self, n: i64) {
+        extensions::u64_to_be_bytes(n as u64, 8u, |v| self.write(v))
+    }
+
+    /// Write a big-endian i32 (4 bytes).
+    fn write_be_i32(&mut self, n: i32) {
+        extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v))
+    }
+
+    /// Write a big-endian i16 (2 bytes).
+    fn write_be_i16(&mut self, n: i16) {
+        extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v))
+    }
+
+    /// Write a big-endian IEEE754 double-precision floating-point (8 bytes).
+    fn write_be_f64(&mut self, f: f64) {
+        unsafe {
+            self.write_be_u64(cast::transmute(f))
+        }
+    }
+
+    /// Write a big-endian IEEE754 single-precision floating-point (4 bytes).
+    fn write_be_f32(&mut self, f: f32) {
+        unsafe {
+            self.write_be_u32(cast::transmute(f))
+        }
+    }
+
+    /// Write a little-endian u64 (8 bytes).
+    fn write_le_u64(&mut self, n: u64) {
+        extensions::u64_to_le_bytes(n, 8u, |v| self.write(v))
+    }
+
+    /// Write a little-endian u32 (4 bytes).
+    fn write_le_u32(&mut self, n: u32) {
+        extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v))
+    }
+
+    /// Write a little-endian u16 (2 bytes).
+    fn write_le_u16(&mut self, n: u16) {
+        extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v))
+    }
+
+    /// Write a little-endian i64 (8 bytes).
+    fn write_le_i64(&mut self, n: i64) {
+        extensions::u64_to_le_bytes(n as u64, 8u, |v| self.write(v))
+    }
+
+    /// Write a little-endian i32 (4 bytes).
+    fn write_le_i32(&mut self, n: i32) {
+        extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v))
+    }
+
+    /// Write a little-endian i16 (2 bytes).
+    fn write_le_i16(&mut self, n: i16) {
+        extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v))
+    }
+
+    /// Write a little-endian IEEE754 double-precision floating-point
+    /// (8 bytes).
+    fn write_le_f64(&mut self, f: f64) {
+        unsafe {
+            self.write_le_u64(cast::transmute(f))
+        }
+    }
+
+    /// Write a little-endian IEEE754 single-precision floating-point
+    /// (4 bytes).
+    fn write_le_f32(&mut self, f: f32) {
+        unsafe {
+            self.write_le_u32(cast::transmute(f))
+        }
+    }
+
+    /// Write a u8 (1 byte).
+    fn write_u8(&mut self, n: u8) {
+        self.write([n])
+    }
+
+    /// Write a i8 (1 byte).
+    fn write_i8(&mut self, n: i8) {
+        self.write([n as u8])
+    }
+}
+
+impl Writer for ~Writer {
+    fn write(&mut self, buf: &[u8]) { self.write(buf) }
+    fn flush(&mut self) { self.flush() }
+}
+
+impl<'self> Writer for &'self mut Writer {
+    fn write(&mut self, buf: &[u8]) { self.write(buf) }
+    fn flush(&mut self) { self.flush() }
+}
+
+pub trait Stream: Reader + Writer { }
+
+impl<T: Reader + Writer> Stream for T {}
+
+pub enum SeekStyle {
+    /// Seek from the beginning of the stream
+    SeekSet,
+    /// Seek from the end of the stream
+    SeekEnd,
+    /// Seek from the current position
+    SeekCur,
+}
+
+/// # XXX
+/// * Are `u64` and `i64` the right choices?
+pub trait Seek {
+    /// Return position of file cursor in the stream
+    fn tell(&self) -> u64;
+
+    /// Seek to an offset in a stream
+    ///
+    /// A successful seek clears the EOF indicator.
+    ///
+    /// # XXX
+    ///
+    /// * What is the behavior when seeking past the end of a stream?
+    fn seek(&mut self, pos: i64, style: SeekStyle);
+}
+
+/// A listener is a value that can consume itself to start listening for connections.
+/// Doing so produces some sort of Acceptor.
+pub trait Listener<T, A: Acceptor<T>> {
+    /// Spin up the listener and start queueing incoming connections
+    ///
+    /// # Failure
+    ///
+    /// Raises `io_error` condition. If the condition is handled,
+    /// then `listen` returns `None`.
+    fn listen(self) -> Option<A>;
+}
+
+/// An acceptor is a value that presents incoming connections
+pub trait Acceptor<T> {
+    /// Wait for and accept an incoming connection
+    ///
+    /// # Failure
+    /// Raise `io_error` condition. If the condition is handled,
+    /// then `accept` returns `None`.
+    fn accept(&mut self) -> Option<T>;
+
+    /// Create an iterator over incoming connection attempts
+    fn incoming<'r>(&'r mut self) -> IncomingIterator<'r, Self> {
+        IncomingIterator { inc: self }
+    }
+}
+
+/// An infinite iterator over incoming connection attempts.
+/// Calling `next` will block the task until a connection is attempted.
+///
+/// Since connection attempts can continue forever, this iterator always returns Some.
+/// The Some contains another Option representing whether the connection attempt was succesful.
+/// A successful connection will be wrapped in Some.
+/// A failed connection is represented as a None and raises a condition.
+struct IncomingIterator<'self, A> {
+    priv inc: &'self mut A,
+}
+
+impl<'self, T, A: Acceptor<T>> Iterator<Option<T>> for IncomingIterator<'self, A> {
+    fn next(&mut self) -> Option<Option<T>> {
+        Some(self.inc.accept())
+    }
+}
+
+/// Common trait for decorator types.
+///
+/// Provides accessors to get the inner, 'decorated' values. The I/O library
+/// uses decorators to add functionality like compression and encryption to I/O
+/// streams.
+///
+/// # XXX
+///
+/// Is this worth having a trait for? May be overkill
+pub trait Decorator<T> {
+    /// Destroy the decorator and extract the decorated value
+    ///
+    /// # XXX
+    ///
+    /// Because this takes `self' one could never 'undecorate' a Reader/Writer
+    /// that has been boxed. Is that ok? This feature is mostly useful for
+    /// extracting the buffer from MemWriter
+    fn inner(self) -> T;
+
+    /// Take an immutable reference to the decorated value
+    fn inner_ref<'a>(&'a self) -> &'a T;
+
+    /// Take a mutable reference to the decorated value
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut T;
+}
+
+pub fn standard_error(kind: IoErrorKind) -> IoError {
+    match kind {
+        PreviousIoError => {
+            IoError {
+                kind: PreviousIoError,
+                desc: "Failing due to a previous I/O error",
+                detail: None
+            }
+        }
+        EndOfFile => {
+            IoError {
+                kind: EndOfFile,
+                desc: "End of file",
+                detail: None
+            }
+        }
+        IoUnavailable => {
+            IoError {
+                kind: IoUnavailable,
+                desc: "I/O is unavailable",
+                detail: None
+            }
+        }
+        _ => fail!()
+    }
+}
+
+pub fn placeholder_error() -> IoError {
+    IoError {
+        kind: OtherIoError,
+        desc: "Placeholder error. You shouldn't be seeing this",
+        detail: None
+    }
+}
+
+/// A mode specifies how a file should be opened or created. These modes are
+/// passed to `File::open_mode` and are used to control where the file is
+/// positioned when it is initially opened.
+pub enum FileMode {
+    /// Opens a file positioned at the beginning.
+    Open,
+    /// Opens a file positioned at EOF.
+    Append,
+    /// Opens a file, truncating it if it already exists.
+    Truncate,
+}
+
+/// Access permissions with which the file should be opened. `File`s
+/// opened with `Read` will raise an `io_error` condition if written to.
+pub enum FileAccess {
+    Read,
+    Write,
+    ReadWrite,
+}
+
+/// Different kinds of files which can be identified by a call to stat
+#[deriving(Eq)]
+pub enum FileType {
+    TypeFile,
+    TypeDirectory,
+    TypeNamedPipe,
+    TypeBlockSpecial,
+    TypeSymlink,
+    TypeUnknown,
+}
+
+pub struct FileStat {
+    /// The path that this stat structure is describing
+    path: Path,
+    /// The size of the file, in bytes
+    size: u64,
+    /// The kind of file this path points to (directory, file, pipe, etc.)
+    kind: FileType,
+    /// The file permissions currently on the file
+    perm: FilePermission,
+
+    // FIXME(#10301): These time fields are pretty useless without an actual
+    //                time representation, what are the milliseconds relative
+    //                to?
+
+    /// The time that the file was created at, in platform-dependent
+    /// milliseconds
+    created: u64,
+    /// The time that this file was last modified, in platform-dependent
+    /// milliseconds
+    modified: u64,
+    /// The time that this file was last accessed, in platform-dependent
+    /// milliseconds
+    accessed: u64,
+
+    /// Information returned by stat() which is not guaranteed to be
+    /// platform-independent. This information may be useful on some platforms,
+    /// but it may have different meanings or no meaning at all on other
+    /// platforms.
+    ///
+    /// Usage of this field is discouraged, but if access is desired then the
+    /// fields are located here.
+    #[unstable]
+    unstable: UnstableFileStat,
+}
+
+/// This structure represents all of the possible information which can be
+/// returned from a `stat` syscall which is not contained in the `FileStat`
+/// structure. This information is not necessarily platform independent, and may
+/// have different meanings or no meaning at all on some platforms.
+#[unstable]
+pub struct UnstableFileStat {
+    device: u64,
+    inode: u64,
+    rdev: u64,
+    nlink: u64,
+    uid: u64,
+    gid: u64,
+    blksize: u64,
+    blocks: u64,
+    flags: u64,
+    gen: u64,
+}
+
+/// A set of permissions for a file or directory is represented by a set of
+/// flags which are or'd together.
+pub type FilePermission = u32;
+
+// Each permission bit
+pub static UserRead: FilePermission     = 0x100;
+pub static UserWrite: FilePermission    = 0x080;
+pub static UserExecute: FilePermission  = 0x040;
+pub static GroupRead: FilePermission    = 0x020;
+pub static GroupWrite: FilePermission   = 0x010;
+pub static GroupExecute: FilePermission = 0x008;
+pub static OtherRead: FilePermission    = 0x004;
+pub static OtherWrite: FilePermission   = 0x002;
+pub static OtherExecute: FilePermission = 0x001;
+
+// Common combinations of these bits
+pub static UserRWX: FilePermission  = UserRead | UserWrite | UserExecute;
+pub static GroupRWX: FilePermission = GroupRead | GroupWrite | GroupExecute;
+pub static OtherRWX: FilePermission = OtherRead | OtherWrite | OtherExecute;
+
+/// A set of permissions for user owned files, this is equivalent to 0644 on
+/// unix-like systems.
+pub static UserFile: FilePermission = UserRead | UserWrite | GroupRead | OtherRead;
+/// A set of permissions for user owned directories, this is equivalent to 0755
+/// on unix-like systems.
+pub static UserDir: FilePermission = UserRWX | GroupRead | GroupExecute |
+                                     OtherRead | OtherExecute;
+/// A set of permissions for user owned executables, this is equivalent to 0755
+/// on unix-like systems.
+pub static UserExec: FilePermission = UserDir;
+
+/// A mask for all possible permission bits
+pub static AllPermissions: FilePermission = 0x1ff;
diff --git a/src/libstd/io/native/file.rs b/src/libstd/io/native/file.rs
new file mode 100644
index 00000000000..0f1a64edb60
--- /dev/null
+++ b/src/libstd/io/native/file.rs
@@ -0,0 +1,761 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Blocking posix-based file I/O
+
+#[allow(non_camel_case_types)];
+
+use libc;
+use os;
+use prelude::*;
+use super::super::*;
+
+#[cfg(windows)]
+fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
+    match errno {
+        libc::EOF => (EndOfFile, "end of file"),
+        _ => (OtherIoError, "unknown error"),
+    }
+}
+
+#[cfg(not(windows))]
+fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
+    // XXX: this should probably be a bit more descriptive...
+    match errno {
+        libc::EOF => (EndOfFile, "end of file"),
+
+        // These two constants can have the same value on some systems, but
+        // different values on others, so we can't use a match clause
+        x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
+            (ResourceUnavailable, "resource temporarily unavailable"),
+
+        _ => (OtherIoError, "unknown error"),
+    }
+}
+
+fn raise_error() {
+    let (kind, desc) = get_err(os::errno() as i32);
+    io_error::cond.raise(IoError {
+        kind: kind,
+        desc: desc,
+        detail: Some(os::last_os_error())
+    });
+}
+
+fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 {
+    #[cfg(windows)] static eintr: int = 0; // doesn't matter
+    #[cfg(not(windows))] static eintr: int = libc::EINTR as int;
+
+    let (data, origamt) = do data.as_imm_buf |data, amt| { (data, amt) };
+    let mut data = data;
+    let mut amt = origamt;
+    while amt > 0 {
+        let mut ret;
+        loop {
+            ret = f(data, amt);
+            if cfg!(not(windows)) { break } // windows has no eintr
+            // if we get an eintr, then try again
+            if ret != -1 || os::errno() as int != eintr { break }
+        }
+        if ret == 0 {
+            break
+        } else if ret != -1 {
+            amt -= ret as uint;
+            data = unsafe { data.offset(ret as int) };
+        } else {
+            return ret;
+        }
+    }
+    return (origamt - amt) as i64;
+}
+
+pub type fd_t = libc::c_int;
+
+pub struct FileDesc {
+    priv fd: fd_t,
+    priv close_on_drop: bool,
+}
+
+impl FileDesc {
+    /// Create a `FileDesc` from an open C file descriptor.
+    ///
+    /// The `FileDesc` will take ownership of the specified file descriptor and
+    /// close it upon destruction if the `close_on_drop` flag is true, otherwise
+    /// it will not close the file descriptor when this `FileDesc` is dropped.
+    ///
+    /// Note that all I/O operations done on this object will be *blocking*, but
+    /// they do not require the runtime to be active.
+    pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
+        FileDesc { fd: fd, close_on_drop: close_on_drop }
+    }
+}
+
+impl Reader for FileDesc {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        #[cfg(windows)] type rlen = libc::c_uint;
+        #[cfg(not(windows))] type rlen = libc::size_t;
+        let ret = do keep_going(buf) |buf, len| {
+            unsafe {
+                libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64
+            }
+        };
+        if ret == 0 {
+            None
+        } else if ret < 0 {
+            raise_error();
+            None
+        } else {
+            Some(ret as uint)
+        }
+    }
+
+    fn eof(&mut self) -> bool { false }
+}
+
+impl Writer for FileDesc {
+    fn write(&mut self, buf: &[u8]) {
+        #[cfg(windows)] type wlen = libc::c_uint;
+        #[cfg(not(windows))] type wlen = libc::size_t;
+        let ret = do keep_going(buf) |buf, len| {
+            unsafe {
+                libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64
+            }
+        };
+        if ret < 0 {
+            raise_error();
+        }
+    }
+}
+
+impl Drop for FileDesc {
+    fn drop(&mut self) {
+        if self.close_on_drop {
+            unsafe { libc::close(self.fd); }
+        }
+    }
+}
+
+pub struct CFile {
+    priv file: *libc::FILE
+}
+
+impl CFile {
+    /// Create a `CFile` from an open `FILE` pointer.
+    ///
+    /// The `CFile` takes ownership of the `FILE` pointer and will close it upon
+    /// destruction.
+    pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } }
+}
+
+impl Reader for CFile {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        let ret = do keep_going(buf) |buf, len| {
+            unsafe {
+                libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
+                            self.file) as i64
+            }
+        };
+        if ret == 0 {
+            None
+        } else if ret < 0 {
+            raise_error();
+            None
+        } else {
+            Some(ret as uint)
+        }
+    }
+
+    fn eof(&mut self) -> bool {
+        unsafe { libc::feof(self.file) != 0 }
+    }
+}
+
+impl Writer for CFile {
+    fn write(&mut self, buf: &[u8]) {
+        let ret = do keep_going(buf) |buf, len| {
+            unsafe {
+                libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t,
+                            self.file) as i64
+            }
+        };
+        if ret < 0 {
+            raise_error();
+        }
+    }
+
+    fn flush(&mut self) {
+        if unsafe { libc::fflush(self.file) } < 0 {
+            raise_error();
+        }
+    }
+}
+
+impl Seek for CFile {
+    fn tell(&self) -> u64 {
+        let ret = unsafe { libc::ftell(self.file) };
+        if ret < 0 {
+            raise_error();
+        }
+        return ret as u64;
+    }
+
+    fn seek(&mut self, pos: i64, style: SeekStyle) {
+        let whence = match style {
+            SeekSet => libc::SEEK_SET,
+            SeekEnd => libc::SEEK_END,
+            SeekCur => libc::SEEK_CUR,
+        };
+        if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 {
+            raise_error();
+        }
+    }
+}
+
+impl Drop for CFile {
+    fn drop(&mut self) {
+        unsafe { libc::fclose(self.file); }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use libc;
+    use os;
+    use prelude::*;
+    use io::{io_error, SeekSet};
+    use super::*;
+
+    #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer
+    fn test_file_desc() {
+        // Run this test with some pipes so we don't have to mess around with
+        // opening or closing files.
+        unsafe {
+            let os::Pipe { input, out } = os::pipe();
+            let mut reader = FileDesc::new(input, true);
+            let mut writer = FileDesc::new(out, true);
+
+            writer.write(bytes!("test"));
+            let mut buf = [0u8, ..4];
+            match reader.read(buf) {
+                Some(4) => {
+                    assert_eq!(buf[0], 't' as u8);
+                    assert_eq!(buf[1], 'e' as u8);
+                    assert_eq!(buf[2], 's' as u8);
+                    assert_eq!(buf[3], 't' as u8);
+                }
+                r => fail!("invalid read: {:?}", r)
+            }
+
+            let mut raised = false;
+            do io_error::cond.trap(|_| { raised = true; }).inside {
+                writer.read(buf);
+            }
+            assert!(raised);
+
+            raised = false;
+            do io_error::cond.trap(|_| { raised = true; }).inside {
+                reader.write(buf);
+            }
+            assert!(raised);
+        }
+    }
+
+    #[ignore(cfg(windows))] // apparently windows doesn't like tmpfile
+    fn test_cfile() {
+        unsafe {
+            let f = libc::tmpfile();
+            assert!(!f.is_null());
+            let mut file = CFile::new(f);
+
+            file.write(bytes!("test"));
+            let mut buf = [0u8, ..4];
+            file.seek(0, SeekSet);
+            match file.read(buf) {
+                Some(4) => {
+                    assert_eq!(buf[0], 't' as u8);
+                    assert_eq!(buf[1], 'e' as u8);
+                    assert_eq!(buf[2], 's' as u8);
+                    assert_eq!(buf[3], 't' as u8);
+                }
+                r => fail!("invalid read: {:?}", r)
+            }
+        }
+    }
+}
+
+// n.b. these functions were all part of the old `std::os` module. There's lots
+//      of fun little nuances that were taken care of by these functions, but
+//      they are all thread-blocking versions that are no longer desired (we now
+//      use a non-blocking event loop implementation backed by libuv).
+//
+//      In theory we will have a thread-blocking version of the event loop (if
+//      desired), so these functions may just need to get adapted to work in
+//      those situtations. For now, I'm leaving the code around so it doesn't
+//      get bitrotted instantaneously.
+mod old_os {
+    use prelude::*;
+    use libc::{size_t, c_void, c_int};
+    use libc;
+    use vec;
+
+    #[cfg(not(windows))] use c_str::CString;
+    #[cfg(not(windows))] use libc::fclose;
+    #[cfg(test)] #[cfg(windows)] use os;
+    #[cfg(test)] use rand;
+    #[cfg(windows)] use str;
+    #[cfg(windows)] use ptr;
+
+    // On Windows, wide character version of function must be used to support
+    // unicode, so functions should be split into at least two versions,
+    // which are for Windows and for non-Windows, if necessary.
+    // See https://github.com/mozilla/rust/issues/9822 for more information.
+
+    mod rustrt {
+        use libc::{c_char, c_int};
+        use libc;
+
+        extern {
+            pub fn rust_path_is_dir(path: *libc::c_char) -> c_int;
+            pub fn rust_path_exists(path: *libc::c_char) -> c_int;
+        }
+
+        // Uses _wstat instead of stat.
+        #[cfg(windows)]
+        extern {
+            pub fn rust_path_is_dir_u16(path: *u16) -> c_int;
+            pub fn rust_path_exists_u16(path: *u16) -> c_int;
+        }
+    }
+
+    /// Recursively walk a directory structure
+    pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool {
+        let r = list_dir(p);
+        r.iter().advance(|q| {
+            let path = &p.join(q);
+            f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p)))
+        })
+    }
+
+    #[cfg(unix)]
+    /// Indicates whether a path represents a directory
+    pub fn path_is_dir(p: &Path) -> bool {
+        unsafe {
+            do p.with_c_str |buf| {
+                rustrt::rust_path_is_dir(buf) != 0 as c_int
+            }
+        }
+    }
+
+
+    #[cfg(windows)]
+    pub fn path_is_dir(p: &Path) -> bool {
+        unsafe {
+            do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| {
+                rustrt::rust_path_is_dir_u16(buf) != 0 as c_int
+            }
+        }
+    }
+
+    #[cfg(unix)]
+    /// Indicates whether a path exists
+    pub fn path_exists(p: &Path) -> bool {
+        unsafe {
+            do p.with_c_str |buf| {
+                rustrt::rust_path_exists(buf) != 0 as c_int
+            }
+        }
+    }
+
+    #[cfg(windows)]
+    pub fn path_exists(p: &Path) -> bool {
+        unsafe {
+            do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| {
+                rustrt::rust_path_exists_u16(buf) != 0 as c_int
+            }
+        }
+    }
+
+    /// Creates a directory at the specified path
+    pub fn make_dir(p: &Path, mode: c_int) -> bool {
+        return mkdir(p, mode);
+
+        #[cfg(windows)]
+        fn mkdir(p: &Path, _mode: c_int) -> bool {
+            unsafe {
+                use os::win32::as_utf16_p;
+                // FIXME: turn mode into something useful? #2623
+                do as_utf16_p(p.as_str().unwrap()) |buf| {
+                    libc::CreateDirectoryW(buf, ptr::mut_null())
+                        != (0 as libc::BOOL)
+                }
+            }
+        }
+
+        #[cfg(unix)]
+        fn mkdir(p: &Path, mode: c_int) -> bool {
+            do p.with_c_str |buf| {
+                unsafe {
+                    libc::mkdir(buf, mode as libc::mode_t) == (0 as c_int)
+                }
+            }
+        }
+    }
+
+    /// Creates a directory with a given mode.
+    /// Returns true iff creation
+    /// succeeded. Also creates all intermediate subdirectories
+    /// if they don't already exist, giving all of them the same mode.
+
+    // tjc: if directory exists but with different permissions,
+    // should we return false?
+    pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool {
+        if path_is_dir(p) {
+            return true;
+        }
+        if p.filename().is_some() {
+            let mut p_ = p.clone();
+            p_.pop();
+            if !mkdir_recursive(&p_, mode) {
+                return false;
+            }
+        }
+        return make_dir(p, mode);
+    }
+
+    /// Lists the contents of a directory
+    ///
+    /// Each resulting Path is a relative path with no directory component.
+    pub fn list_dir(p: &Path) -> ~[Path] {
+        unsafe {
+            #[cfg(target_os = "linux")]
+            #[cfg(target_os = "android")]
+            #[cfg(target_os = "freebsd")]
+            #[cfg(target_os = "macos")]
+            unsafe fn get_list(p: &Path) -> ~[Path] {
+                use libc::{dirent_t};
+                use libc::{opendir, readdir, closedir};
+                extern {
+                    fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char;
+                }
+                let mut paths = ~[];
+                debug!("os::list_dir -- BEFORE OPENDIR");
+
+                let dir_ptr = do p.with_c_str |buf| {
+                    opendir(buf)
+                };
+
+                if (dir_ptr as uint != 0) {
+                    debug!("os::list_dir -- opendir() SUCCESS");
+                    let mut entry_ptr = readdir(dir_ptr);
+                    while (entry_ptr as uint != 0) {
+                        let cstr = CString::new(rust_list_dir_val(entry_ptr), false);
+                        paths.push(Path::new(cstr));
+                        entry_ptr = readdir(dir_ptr);
+                    }
+                    closedir(dir_ptr);
+                }
+                else {
+                    debug!("os::list_dir -- opendir() FAILURE");
+                }
+                debug!("os::list_dir -- AFTER -- \\#: {}", paths.len());
+                paths
+            }
+            #[cfg(windows)]
+            unsafe fn get_list(p: &Path) -> ~[Path] {
+                use libc::consts::os::extra::INVALID_HANDLE_VALUE;
+                use libc::{wcslen, free};
+                use libc::funcs::extra::kernel32::{
+                    FindFirstFileW,
+                    FindNextFileW,
+                    FindClose,
+                };
+                use libc::types::os::arch::extra::HANDLE;
+                use os::win32::{
+                    as_utf16_p
+                };
+                use rt::global_heap::malloc_raw;
+
+                #[nolink]
+                extern {
+                    fn rust_list_dir_wfd_size() -> libc::size_t;
+                    fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
+                }
+                let star = p.join("*");
+                do as_utf16_p(star.as_str().unwrap()) |path_ptr| {
+                    let mut paths = ~[];
+                    let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
+                    let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE);
+                    if find_handle as libc::c_int != INVALID_HANDLE_VALUE {
+                        let mut more_files = 1 as libc::c_int;
+                        while more_files != 0 {
+                            let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr);
+                            if fp_buf as uint == 0 {
+                                fail!("os::list_dir() failure: got null ptr from wfd");
+                            }
+                            else {
+                                let fp_vec = vec::from_buf(
+                                    fp_buf, wcslen(fp_buf) as uint);
+                                let fp_str = str::from_utf16(fp_vec);
+                                paths.push(Path::new(fp_str));
+                            }
+                            more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE);
+                        }
+                        FindClose(find_handle);
+                        free(wfd_ptr)
+                    }
+                    paths
+                }
+            }
+            do get_list(p).move_iter().filter |path| {
+                path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
+            }.collect()
+        }
+    }
+
+    /// Removes a directory at the specified path, after removing
+    /// all its contents. Use carefully!
+    pub fn remove_dir_recursive(p: &Path) -> bool {
+        let mut error_happened = false;
+        do walk_dir(p) |inner| {
+            if !error_happened {
+                if path_is_dir(inner) {
+                    if !remove_dir_recursive(inner) {
+                        error_happened = true;
+                    }
+                }
+                else {
+                    if !remove_file(inner) {
+                        error_happened = true;
+                    }
+                }
+            }
+            true
+        };
+        // Directory should now be empty
+        !error_happened && remove_dir(p)
+    }
+
+    /// Removes a directory at the specified path
+    pub fn remove_dir(p: &Path) -> bool {
+       return rmdir(p);
+
+        #[cfg(windows)]
+        fn rmdir(p: &Path) -> bool {
+            unsafe {
+                use os::win32::as_utf16_p;
+                return do as_utf16_p(p.as_str().unwrap()) |buf| {
+                    libc::RemoveDirectoryW(buf) != (0 as libc::BOOL)
+                };
+            }
+        }
+
+        #[cfg(unix)]
+        fn rmdir(p: &Path) -> bool {
+            do p.with_c_str |buf| {
+                unsafe {
+                    libc::rmdir(buf) == (0 as c_int)
+                }
+            }
+        }
+    }
+
+    /// Deletes an existing file
+    pub fn remove_file(p: &Path) -> bool {
+        return unlink(p);
+
+        #[cfg(windows)]
+        fn unlink(p: &Path) -> bool {
+            unsafe {
+                use os::win32::as_utf16_p;
+                return do as_utf16_p(p.as_str().unwrap()) |buf| {
+                    libc::DeleteFileW(buf) != (0 as libc::BOOL)
+                };
+            }
+        }
+
+        #[cfg(unix)]
+        fn unlink(p: &Path) -> bool {
+            unsafe {
+                do p.with_c_str |buf| {
+                    libc::unlink(buf) == (0 as c_int)
+                }
+            }
+        }
+    }
+
+    /// Renames an existing file or directory
+    pub fn rename_file(old: &Path, new: &Path) -> bool {
+        unsafe {
+           do old.with_c_str |old_buf| {
+                do new.with_c_str |new_buf| {
+                    libc::rename(old_buf, new_buf) == (0 as c_int)
+                }
+           }
+        }
+    }
+
+    /// Copies a file from one location to another
+    pub fn copy_file(from: &Path, to: &Path) -> bool {
+        return do_copy_file(from, to);
+
+        #[cfg(windows)]
+        fn do_copy_file(from: &Path, to: &Path) -> bool {
+            unsafe {
+                use os::win32::as_utf16_p;
+                return do as_utf16_p(from.as_str().unwrap()) |fromp| {
+                    do as_utf16_p(to.as_str().unwrap()) |top| {
+                        libc::CopyFileW(fromp, top, (0 as libc::BOOL)) !=
+                            (0 as libc::BOOL)
+                    }
+                }
+            }
+        }
+
+        #[cfg(unix)]
+        fn do_copy_file(from: &Path, to: &Path) -> bool {
+            unsafe {
+                let istream = do from.with_c_str |fromp| {
+                    do "rb".with_c_str |modebuf| {
+                        libc::fopen(fromp, modebuf)
+                    }
+                };
+                if istream as uint == 0u {
+                    return false;
+                }
+                // Preserve permissions
+                let from_mode = from.stat().perm;
+
+                let ostream = do to.with_c_str |top| {
+                    do "w+b".with_c_str |modebuf| {
+                        libc::fopen(top, modebuf)
+                    }
+                };
+                if ostream as uint == 0u {
+                    fclose(istream);
+                    return false;
+                }
+                let bufsize = 8192u;
+                let mut buf = vec::with_capacity::<u8>(bufsize);
+                let mut done = false;
+                let mut ok = true;
+                while !done {
+                    do buf.as_mut_buf |b, _sz| {
+                      let nread = libc::fread(b as *mut c_void, 1u as size_t,
+                                              bufsize as size_t,
+                                              istream);
+                      if nread > 0 as size_t {
+                          if libc::fwrite(b as *c_void, 1u as size_t, nread,
+                                          ostream) != nread {
+                              ok = false;
+                              done = true;
+                          }
+                      } else {
+                          done = true;
+                      }
+                  }
+                }
+                fclose(istream);
+                fclose(ostream);
+
+                // Give the new file the old file's permissions
+                if do to.with_c_str |to_buf| {
+                    libc::chmod(to_buf, from_mode as libc::mode_t)
+                } != 0 {
+                    return false; // should be a condition...
+                }
+                return ok;
+            }
+        }
+    }
+
+    #[test]
+    fn tmpdir() {
+        let p = os::tmpdir();
+        let s = p.as_str();
+        assert!(s.is_some() && s.unwrap() != ".");
+    }
+
+    // Issue #712
+    #[test]
+    fn test_list_dir_no_invalid_memory_access() {
+        list_dir(&Path::new("."));
+    }
+
+    #[test]
+    fn test_list_dir() {
+        let dirs = list_dir(&Path::new("."));
+        // Just assuming that we've got some contents in the current directory
+        assert!(dirs.len() > 0u);
+
+        for dir in dirs.iter() {
+            debug!("{:?}", (*dir).clone());
+        }
+    }
+
+    #[test]
+    #[cfg(not(windows))]
+    fn test_list_dir_root() {
+        let dirs = list_dir(&Path::new("/"));
+        assert!(dirs.len() > 1);
+    }
+    #[test]
+    #[cfg(windows)]
+    fn test_list_dir_root() {
+        let dirs = list_dir(&Path::new("C:\\"));
+        assert!(dirs.len() > 1);
+    }
+
+    #[test]
+    fn test_path_is_dir() {
+        use io::fs::{mkdir_recursive};
+        use io::{File, UserRWX};
+
+        assert!((path_is_dir(&Path::new("."))));
+        assert!((!path_is_dir(&Path::new("test/stdtest/fs.rs"))));
+
+        let mut dirpath = os::tmpdir();
+        dirpath.push(format!("rust-test-{}/test-\uac00\u4e00\u30fc\u4f60\u597d",
+            rand::random::<u32>())); // 가一ー你好
+        debug!("path_is_dir dirpath: {}", dirpath.display());
+
+        mkdir_recursive(&dirpath, UserRWX);
+
+        assert!((path_is_dir(&dirpath)));
+
+        let mut filepath = dirpath;
+        filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs");
+        debug!("path_is_dir filepath: {}", filepath.display());
+
+        File::create(&filepath); // ignore return; touch only
+        assert!((!path_is_dir(&filepath)));
+
+        assert!((!path_is_dir(&Path::new(
+                     "test/unicode-bogus-dir-\uac00\u4e00\u30fc\u4f60\u597d"))));
+    }
+
+    #[test]
+    fn test_path_exists() {
+        use io::fs::mkdir_recursive;
+        use io::UserRWX;
+
+        assert!((path_exists(&Path::new("."))));
+        assert!((!path_exists(&Path::new(
+                     "test/nonexistent-bogus-path"))));
+
+        let mut dirpath = os::tmpdir();
+        dirpath.push(format!("rust-test-{}/test-\uac01\u4e01\u30fc\u518d\u89c1",
+            rand::random::<u32>())); // 각丁ー再见
+
+        mkdir_recursive(&dirpath, UserRWX);
+        assert!((path_exists(&dirpath)));
+        assert!((!path_exists(&Path::new(
+                     "test/unicode-bogus-path-\uac01\u4e01\u30fc\u518d\u89c1"))));
+    }
+}
diff --git a/src/libstd/io/native/process.rs b/src/libstd/io/native/process.rs
new file mode 100644
index 00000000000..de03ac1c07d
--- /dev/null
+++ b/src/libstd/io/native/process.rs
@@ -0,0 +1,734 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use cast;
+use libc::{pid_t, c_void, c_int};
+use libc;
+use os;
+use prelude::*;
+use ptr;
+use io;
+use super::file;
+
+/**
+ * A value representing a child process.
+ *
+ * The lifetime of this value is linked to the lifetime of the actual
+ * process - the Process destructor calls self.finish() which waits
+ * for the process to terminate.
+ */
+pub struct Process {
+    /// The unique id of the process (this should never be negative).
+    priv pid: pid_t,
+
+    /// A handle to the process - on unix this will always be NULL, but on
+    /// windows it will be a HANDLE to the process, which will prevent the
+    /// pid being re-used until the handle is closed.
+    priv handle: *(),
+
+    /// Currently known stdin of the child, if any
+    priv input: Option<file::FileDesc>,
+    /// Currently known stdout of the child, if any
+    priv output: Option<file::FileDesc>,
+    /// Currently known stderr of the child, if any
+    priv error: Option<file::FileDesc>,
+
+    /// None until finish() is called.
+    priv exit_code: Option<int>,
+}
+
+impl Process {
+    /// Creates a new process using native process-spawning abilities provided
+    /// by the OS. Operations on this process will be blocking instead of using
+    /// the runtime for sleeping just this current task.
+    ///
+    /// # Arguments
+    ///
+    /// * prog - the program to run
+    /// * args - the arguments to pass to the program, not including the program
+    ///          itself
+    /// * env - an optional envrionment to specify for the child process. If
+    ///         this value is `None`, then the child will inherit the parent's
+    ///         environment
+    /// * cwd - an optionally specified current working directory of the child,
+    ///         defaulting to the parent's current working directory
+    /// * stdin, stdout, stderr - These optionally specified file descriptors
+    ///     dictate where the stdin/out/err of the child process will go. If
+    ///     these are `None`, then this module will bind the input/output to an
+    ///     os pipe instead. This process takes ownership of these file
+    ///     descriptors, closing them upon destruction of the process.
+    pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>,
+               cwd: Option<&Path>,
+               stdin: Option<file::fd_t>,
+               stdout: Option<file::fd_t>,
+               stderr: Option<file::fd_t>) -> Process {
+        let (in_pipe, in_fd) = match stdin {
+            None => {
+                let pipe = os::pipe();
+                (Some(pipe), pipe.input)
+            },
+            Some(fd) => (None, fd)
+        };
+        let (out_pipe, out_fd) = match stdout {
+            None => {
+                let pipe = os::pipe();
+                (Some(pipe), pipe.out)
+            },
+            Some(fd) => (None, fd)
+        };
+        let (err_pipe, err_fd) = match stderr {
+            None => {
+                let pipe = os::pipe();
+                (Some(pipe), pipe.out)
+            },
+            Some(fd) => (None, fd)
+        };
+
+        let res = spawn_process_os(prog, args, env, cwd,
+                                   in_fd, out_fd, err_fd);
+
+        unsafe {
+            for pipe in in_pipe.iter() { libc::close(pipe.input); }
+            for pipe in out_pipe.iter() { libc::close(pipe.out); }
+            for pipe in err_pipe.iter() { libc::close(pipe.out); }
+        }
+
+        Process {
+            pid: res.pid,
+            handle: res.handle,
+            input: in_pipe.map(|pipe| file::FileDesc::new(pipe.out, true)),
+            output: out_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)),
+            error: err_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)),
+            exit_code: None,
+        }
+    }
+
+    /// Returns the unique id of the process
+    pub fn id(&self) -> pid_t { self.pid }
+
+    /**
+     * Returns an io::Writer that can be used to write to this Process's stdin.
+     *
+     * Fails if there is no stdinavailable (it's already been removed by
+     * take_input)
+     */
+    pub fn input<'a>(&'a mut self) -> &'a mut io::Writer {
+        match self.input {
+            Some(ref mut fd) => fd as &mut io::Writer,
+            None => fail!("This process has no stdin")
+        }
+    }
+
+    /**
+     * Returns an io::Reader that can be used to read from this Process's
+     * stdout.
+     *
+     * Fails if there is no stdin available (it's already been removed by
+     * take_output)
+     */
+    pub fn output<'a>(&'a mut self) -> &'a mut io::Reader {
+        match self.input {
+            Some(ref mut fd) => fd as &mut io::Reader,
+            None => fail!("This process has no stdout")
+        }
+    }
+
+    /**
+     * Returns an io::Reader that can be used to read from this Process's
+     * stderr.
+     *
+     * Fails if there is no stdin available (it's already been removed by
+     * take_error)
+     */
+    pub fn error<'a>(&'a mut self) -> &'a mut io::Reader {
+        match self.error {
+            Some(ref mut fd) => fd as &mut io::Reader,
+            None => fail!("This process has no stderr")
+        }
+    }
+
+    /**
+     * Takes the stdin of this process, transferring ownership to the caller.
+     * Note that when the return value is destroyed, the handle will be closed
+     * for the child process.
+     */
+    pub fn take_input(&mut self) -> Option<~io::Writer> {
+        self.input.take().map(|fd| ~fd as ~io::Writer)
+    }
+
+    /**
+     * Takes the stdout of this process, transferring ownership to the caller.
+     * Note that when the return value is destroyed, the handle will be closed
+     * for the child process.
+     */
+    pub fn take_output(&mut self) -> Option<~io::Reader> {
+        self.output.take().map(|fd| ~fd as ~io::Reader)
+    }
+
+    /**
+     * Takes the stderr of this process, transferring ownership to the caller.
+     * Note that when the return value is destroyed, the handle will be closed
+     * for the child process.
+     */
+    pub fn take_error(&mut self) -> Option<~io::Reader> {
+        self.error.take().map(|fd| ~fd as ~io::Reader)
+    }
+
+    pub fn wait(&mut self) -> int {
+        for &code in self.exit_code.iter() {
+            return code;
+        }
+        let code = waitpid(self.pid);
+        self.exit_code = Some(code);
+        return code;
+    }
+
+    pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> {
+        // if the process has finished, and therefore had waitpid called,
+        // and we kill it, then on unix we might ending up killing a
+        // newer process that happens to have the same (re-used) id
+        match self.exit_code {
+            Some(*) => return Err(io::IoError {
+                kind: io::OtherIoError,
+                desc: "can't kill an exited process",
+                detail: None,
+            }),
+            None => {}
+        }
+        return unsafe { killpid(self.pid, signum) };
+
+        #[cfg(windows)]
+        unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
+            match signal {
+                io::process::PleaseExitSignal |
+                io::process::MustDieSignal => {
+                    libc::funcs::extra::kernel32::TerminateProcess(
+                        cast::transmute(pid), 1);
+                    Ok(())
+                }
+                _ => Err(io::IoError {
+                    kind: io::OtherIoError,
+                    desc: "unsupported signal on windows",
+                    detail: None,
+                })
+            }
+        }
+
+        #[cfg(not(windows))]
+        unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
+            libc::funcs::posix88::signal::kill(pid, signal as c_int);
+            Ok(())
+        }
+    }
+}
+
+impl Drop for Process {
+    fn drop(&mut self) {
+        // close all these handles
+        self.take_input();
+        self.take_output();
+        self.take_error();
+        self.wait();
+        free_handle(self.handle);
+    }
+}
+
+struct SpawnProcessResult {
+    pid: pid_t,
+    handle: *(),
+}
+
+#[cfg(windows)]
+fn spawn_process_os(prog: &str, args: &[~str],
+                    env: Option<~[(~str, ~str)]>,
+                    dir: Option<&Path>,
+                    in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
+    use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
+    use libc::consts::os::extra::{
+        TRUE, FALSE,
+        STARTF_USESTDHANDLES,
+        INVALID_HANDLE_VALUE,
+        DUPLICATE_SAME_ACCESS
+    };
+    use libc::funcs::extra::kernel32::{
+        GetCurrentProcess,
+        DuplicateHandle,
+        CloseHandle,
+        CreateProcessA
+    };
+    use libc::funcs::extra::msvcrt::get_osfhandle;
+
+    use mem;
+
+    unsafe {
+
+        let mut si = zeroed_startupinfo();
+        si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
+        si.dwFlags = STARTF_USESTDHANDLES;
+
+        let cur_proc = GetCurrentProcess();
+
+        let orig_std_in = get_osfhandle(in_fd) as HANDLE;
+        if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
+            fail!("failure in get_osfhandle: {}", os::last_os_error());
+        }
+        if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput,
+                           0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+            fail!("failure in DuplicateHandle: {}", os::last_os_error());
+        }
+
+        let orig_std_out = get_osfhandle(out_fd) as HANDLE;
+        if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
+            fail!("failure in get_osfhandle: {}", os::last_os_error());
+        }
+        if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput,
+                           0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+            fail!("failure in DuplicateHandle: {}", os::last_os_error());
+        }
+
+        let orig_std_err = get_osfhandle(err_fd) as HANDLE;
+        if orig_std_err == INVALID_HANDLE_VALUE as HANDLE {
+            fail!("failure in get_osfhandle: {}", os::last_os_error());
+        }
+        if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError,
+                           0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+            fail!("failure in DuplicateHandle: {}", os::last_os_error());
+        }
+
+        let cmd = make_command_line(prog, args);
+        let mut pi = zeroed_process_information();
+        let mut create_err = None;
+
+        do with_envp(env) |envp| {
+            do with_dirp(dir) |dirp| {
+                do cmd.with_c_str |cmdp| {
+                    let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
+                                                 ptr::mut_null(), ptr::mut_null(), TRUE,
+                                                 0, envp, dirp, &mut si, &mut pi);
+                    if created == FALSE {
+                        create_err = Some(os::last_os_error());
+                    }
+                }
+            }
+        }
+
+        CloseHandle(si.hStdInput);
+        CloseHandle(si.hStdOutput);
+        CloseHandle(si.hStdError);
+
+        for msg in create_err.iter() {
+            fail!("failure in CreateProcess: {}", *msg);
+        }
+
+        // We close the thread handle because we don't care about keeping the
+        // thread id valid, and we aren't keeping the thread handle around to be
+        // able to close it later. We don't close the process handle however
+        // because we want the process id to stay valid at least until the
+        // calling code closes the process handle.
+        CloseHandle(pi.hThread);
+
+        SpawnProcessResult {
+            pid: pi.dwProcessId as pid_t,
+            handle: pi.hProcess as *()
+        }
+    }
+}
+
+#[cfg(windows)]
+fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
+    libc::types::os::arch::extra::STARTUPINFO {
+        cb: 0,
+        lpReserved: ptr::mut_null(),
+        lpDesktop: ptr::mut_null(),
+        lpTitle: ptr::mut_null(),
+        dwX: 0,
+        dwY: 0,
+        dwXSize: 0,
+        dwYSize: 0,
+        dwXCountChars: 0,
+        dwYCountCharts: 0,
+        dwFillAttribute: 0,
+        dwFlags: 0,
+        wShowWindow: 0,
+        cbReserved2: 0,
+        lpReserved2: ptr::mut_null(),
+        hStdInput: ptr::mut_null(),
+        hStdOutput: ptr::mut_null(),
+        hStdError: ptr::mut_null()
+    }
+}
+
+#[cfg(windows)]
+fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
+    libc::types::os::arch::extra::PROCESS_INFORMATION {
+        hProcess: ptr::mut_null(),
+        hThread: ptr::mut_null(),
+        dwProcessId: 0,
+        dwThreadId: 0
+    }
+}
+
+// FIXME: this is only pub so it can be tested (see issue #4536)
+#[cfg(windows)]
+pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
+    let mut cmd = ~"";
+    append_arg(&mut cmd, prog);
+    for arg in args.iter() {
+        cmd.push_char(' ');
+        append_arg(&mut cmd, *arg);
+    }
+    return cmd;
+
+    fn append_arg(cmd: &mut ~str, arg: &str) {
+        let quote = arg.iter().any(|c| c == ' ' || c == '\t');
+        if quote {
+            cmd.push_char('"');
+        }
+        for i in range(0u, arg.len()) {
+            append_char_at(cmd, arg, i);
+        }
+        if quote {
+            cmd.push_char('"');
+        }
+    }
+
+    fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) {
+        match arg[i] as char {
+            '"' => {
+                // Escape quotes.
+                cmd.push_str("\\\"");
+            }
+            '\\' => {
+                if backslash_run_ends_in_quote(arg, i) {
+                    // Double all backslashes that are in runs before quotes.
+                    cmd.push_str("\\\\");
+                } else {
+                    // Pass other backslashes through unescaped.
+                    cmd.push_char('\\');
+                }
+            }
+            c => {
+                cmd.push_char(c);
+            }
+        }
+    }
+
+    fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool {
+        while i < s.len() && s[i] as char == '\\' {
+            i += 1;
+        }
+        return i < s.len() && s[i] as char == '"';
+    }
+}
+
+#[cfg(unix)]
+fn spawn_process_os(prog: &str, args: &[~str],
+                    env: Option<~[(~str, ~str)]>,
+                    dir: Option<&Path>,
+                    in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
+    use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
+    use libc::funcs::bsd44::getdtablesize;
+
+    mod rustrt {
+        #[abi = "cdecl"]
+        extern {
+            pub fn rust_unset_sigprocmask();
+        }
+    }
+
+    #[cfg(windows)]
+    unsafe fn set_environ(_envp: *c_void) {}
+    #[cfg(target_os = "macos")]
+    unsafe fn set_environ(envp: *c_void) {
+        extern { fn _NSGetEnviron() -> *mut *c_void; }
+
+        *_NSGetEnviron() = envp;
+    }
+    #[cfg(not(target_os = "macos"), not(windows))]
+    unsafe fn set_environ(envp: *c_void) {
+        extern {
+            static mut environ: *c_void;
+        }
+        environ = envp;
+    }
+
+    unsafe {
+
+        let pid = fork();
+        if pid < 0 {
+            fail!("failure in fork: {}", os::last_os_error());
+        } else if pid > 0 {
+            return SpawnProcessResult {pid: pid, handle: ptr::null()};
+        }
+
+        rustrt::rust_unset_sigprocmask();
+
+        if dup2(in_fd, 0) == -1 {
+            fail!("failure in dup2(in_fd, 0): {}", os::last_os_error());
+        }
+        if dup2(out_fd, 1) == -1 {
+            fail!("failure in dup2(out_fd, 1): {}", os::last_os_error());
+        }
+        if dup2(err_fd, 2) == -1 {
+            fail!("failure in dup3(err_fd, 2): {}", os::last_os_error());
+        }
+        // close all other fds
+        for fd in range(3, getdtablesize()).invert() {
+            close(fd as c_int);
+        }
+
+        do with_dirp(dir) |dirp| {
+            if !dirp.is_null() && chdir(dirp) == -1 {
+                fail!("failure in chdir: {}", os::last_os_error());
+            }
+        }
+
+        do with_envp(env) |envp| {
+            if !envp.is_null() {
+                set_environ(envp);
+            }
+            do with_argv(prog, args) |argv| {
+                execvp(*argv, argv);
+                // execvp only returns if an error occurred
+                fail!("failure in execvp: {}", os::last_os_error());
+            }
+        }
+    }
+}
+
+#[cfg(unix)]
+fn with_argv<T>(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T {
+    use vec;
+
+    // We can't directly convert `str`s into `*char`s, as someone needs to hold
+    // a reference to the intermediary byte buffers. So first build an array to
+    // hold all the ~[u8] byte strings.
+    let mut tmps = vec::with_capacity(args.len() + 1);
+
+    tmps.push(prog.to_c_str());
+
+    for arg in args.iter() {
+        tmps.push(arg.to_c_str());
+    }
+
+    // Next, convert each of the byte strings into a pointer. This is
+    // technically unsafe as the caller could leak these pointers out of our
+    // scope.
+    let mut ptrs = do tmps.map |tmp| {
+        tmp.with_ref(|buf| buf)
+    };
+
+    // Finally, make sure we add a null pointer.
+    ptrs.push(ptr::null());
+
+    ptrs.as_imm_buf(|buf, _| cb(buf))
+}
+
+#[cfg(unix)]
+fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T {
+    use vec;
+
+    // On posixy systems we can pass a char** for envp, which is a
+    // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to
+    // have a temporary buffer to hold the intermediary `~[u8]` byte strings.
+    match env {
+        Some(env) => {
+            let mut tmps = vec::with_capacity(env.len());
+
+            for pair in env.iter() {
+                let kv = format!("{}={}", pair.first(), pair.second());
+                tmps.push(kv.to_c_str());
+            }
+
+            // Once again, this is unsafe.
+            let mut ptrs = do tmps.map |tmp| {
+                tmp.with_ref(|buf| buf)
+            };
+            ptrs.push(ptr::null());
+
+            do ptrs.as_imm_buf |buf, _| {
+                unsafe { cb(cast::transmute(buf)) }
+            }
+        }
+        _ => cb(ptr::null())
+    }
+}
+
+#[cfg(windows)]
+fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T {
+    // On win32 we pass an "environment block" which is not a char**, but
+    // rather a concatenation of null-terminated k=v\0 sequences, with a final
+    // \0 to terminate.
+    match env {
+        Some(env) => {
+            let mut blk = ~[];
+
+            for pair in env.iter() {
+                let kv = format!("{}={}", pair.first(), pair.second());
+                blk.push_all(kv.as_bytes());
+                blk.push(0);
+            }
+
+            blk.push(0);
+
+            do blk.as_imm_buf |p, _len| {
+                unsafe { cb(cast::transmute(p)) }
+            }
+        }
+        _ => cb(ptr::mut_null())
+    }
+}
+
+fn with_dirp<T>(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T {
+    match d {
+      Some(dir) => dir.with_c_str(|buf| cb(buf)),
+      None => cb(ptr::null())
+    }
+}
+
+#[cfg(windows)]
+fn free_handle(handle: *()) {
+    unsafe {
+        libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
+    }
+}
+
+#[cfg(unix)]
+fn free_handle(_handle: *()) {
+    // unix has no process handle object, just a pid
+}
+
+/**
+ * Waits for a process to exit and returns the exit code, failing
+ * if there is no process with the specified id.
+ *
+ * Note that this is private to avoid race conditions on unix where if
+ * a user calls waitpid(some_process.get_id()) then some_process.finish()
+ * and some_process.destroy() and some_process.finalize() will then either
+ * operate on a none-existent process or, even worse, on a newer process
+ * with the same id.
+ */
+fn waitpid(pid: pid_t) -> int {
+    return waitpid_os(pid);
+
+    #[cfg(windows)]
+    fn waitpid_os(pid: pid_t) -> int {
+        use libc::types::os::arch::extra::DWORD;
+        use libc::consts::os::extra::{
+            SYNCHRONIZE,
+            PROCESS_QUERY_INFORMATION,
+            FALSE,
+            STILL_ACTIVE,
+            INFINITE,
+            WAIT_FAILED
+        };
+        use libc::funcs::extra::kernel32::{
+            OpenProcess,
+            GetExitCodeProcess,
+            CloseHandle,
+            WaitForSingleObject
+        };
+
+        unsafe {
+
+            let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
+                                      FALSE,
+                                      pid as DWORD);
+            if process.is_null() {
+                fail!("failure in OpenProcess: {}", os::last_os_error());
+            }
+
+            loop {
+                let mut status = 0;
+                if GetExitCodeProcess(process, &mut status) == FALSE {
+                    CloseHandle(process);
+                    fail!("failure in GetExitCodeProcess: {}", os::last_os_error());
+                }
+                if status != STILL_ACTIVE {
+                    CloseHandle(process);
+                    return status as int;
+                }
+                if WaitForSingleObject(process, INFINITE) == WAIT_FAILED {
+                    CloseHandle(process);
+                    fail!("failure in WaitForSingleObject: {}", os::last_os_error());
+                }
+            }
+        }
+    }
+
+    #[cfg(unix)]
+    fn waitpid_os(pid: pid_t) -> int {
+        use libc::funcs::posix01::wait::*;
+
+        #[cfg(target_os = "linux")]
+        #[cfg(target_os = "android")]
+        fn WIFEXITED(status: i32) -> bool {
+            (status & 0xffi32) == 0i32
+        }
+
+        #[cfg(target_os = "macos")]
+        #[cfg(target_os = "freebsd")]
+        fn WIFEXITED(status: i32) -> bool {
+            (status & 0x7fi32) == 0i32
+        }
+
+        #[cfg(target_os = "linux")]
+        #[cfg(target_os = "android")]
+        fn WEXITSTATUS(status: i32) -> i32 {
+            (status >> 8i32) & 0xffi32
+        }
+
+        #[cfg(target_os = "macos")]
+        #[cfg(target_os = "freebsd")]
+        fn WEXITSTATUS(status: i32) -> i32 {
+            status >> 8i32
+        }
+
+        let mut status = 0 as c_int;
+        if unsafe { waitpid(pid, &mut status, 0) } == -1 {
+            fail!("failure in waitpid: {}", os::last_os_error());
+        }
+
+        return if WIFEXITED(status) {
+            WEXITSTATUS(status) as int
+        } else {
+            1
+        };
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    #[test] #[cfg(windows)]
+    fn test_make_command_line() {
+        use super::make_command_line;
+        assert_eq!(
+            make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]),
+            ~"prog aaa bbb ccc"
+        );
+        assert_eq!(
+            make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]),
+            ~"\"C:\\Program Files\\blah\\blah.exe\" aaa"
+        );
+        assert_eq!(
+            make_command_line("C:\\Program Files\\test", [~"aa\"bb"]),
+            ~"\"C:\\Program Files\\test\" aa\\\"bb"
+        );
+        assert_eq!(
+            make_command_line("echo", [~"a b c"]),
+            ~"echo \"a b c\""
+        );
+    }
+
+    // Currently most of the tests of this functionality live inside std::run,
+    // but they may move here eventually as a non-blocking backend is added to
+    // std::run
+}
diff --git a/src/libstd/io/native/stdio.rs b/src/libstd/io/native/stdio.rs
new file mode 100644
index 00000000000..68748ab49a3
--- /dev/null
+++ b/src/libstd/io/native/stdio.rs
@@ -0,0 +1,63 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use libc;
+use option::Option;
+use io::{Reader, Writer};
+use super::file;
+
+/// Creates a new handle to the stdin of this process
+pub fn stdin() -> StdIn { StdIn::new() }
+/// Creates a new handle to the stdout of this process
+pub fn stdout() -> StdOut { StdOut::new(libc::STDOUT_FILENO) }
+/// Creates a new handle to the stderr of this process
+pub fn stderr() -> StdOut { StdOut::new(libc::STDERR_FILENO) }
+
+pub fn print(s: &str) {
+    stdout().write(s.as_bytes())
+}
+
+pub fn println(s: &str) {
+    let mut out = stdout();
+    out.write(s.as_bytes());
+    out.write(['\n' as u8]);
+}
+
+pub struct StdIn {
+    priv fd: file::FileDesc
+}
+
+impl StdIn {
+    /// Duplicates the stdin file descriptor, returning an io::Reader
+    pub fn new() -> StdIn {
+        StdIn { fd: file::FileDesc::new(libc::STDIN_FILENO, false) }
+    }
+}
+
+impl Reader for StdIn {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.fd.read(buf) }
+    fn eof(&mut self) -> bool { self.fd.eof() }
+}
+
+pub struct StdOut {
+    priv fd: file::FileDesc
+}
+
+impl StdOut {
+    /// Duplicates the specified file descriptor, returning an io::Writer
+    pub fn new(fd: file::fd_t) -> StdOut {
+        StdOut { fd: file::FileDesc::new(fd, false) }
+    }
+}
+
+impl Writer for StdOut {
+    fn write(&mut self, buf: &[u8]) { self.fd.write(buf) }
+    fn flush(&mut self) { self.fd.flush() }
+}
diff --git a/src/libstd/io/net/addrinfo.rs b/src/libstd/io/net/addrinfo.rs
new file mode 100644
index 00000000000..7e2d1fc5e83
--- /dev/null
+++ b/src/libstd/io/net/addrinfo.rs
@@ -0,0 +1,128 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+Synchronous DNS Resolution
+
+Contains the functionality to perform DNS resolution in a style related to
+getaddrinfo()
+
+*/
+
+use option::{Option, Some, None};
+use result::{Ok, Err};
+use io::{io_error};
+use io::net::ip::{SocketAddr, IpAddr};
+use rt::rtio::{IoFactory, with_local_io};
+use vec::ImmutableVector;
+
+/// Hints to the types of sockets that are desired when looking up hosts
+pub enum SocketType {
+    Stream, Datagram, Raw
+}
+
+/// Flags which can be or'd into the `flags` field of a `Hint`. These are used
+/// to manipulate how a query is performed.
+///
+/// The meaning of each of these flags can be found with `man -s 3 getaddrinfo`
+pub enum Flag {
+    AddrConfig,
+    All,
+    CanonName,
+    NumericHost,
+    NumericServ,
+    Passive,
+    V4Mapped,
+}
+
+/// A transport protocol associated with either a hint or a return value of
+/// `lookup`
+pub enum Protocol {
+    TCP, UDP
+}
+
+/// This structure is used to provide hints when fetching addresses for a
+/// remote host to control how the lookup is performed.
+///
+/// For details on these fields, see their corresponding definitions via
+/// `man -s 3 getaddrinfo`
+pub struct Hint {
+    family: uint,
+    socktype: Option<SocketType>,
+    protocol: Option<Protocol>,
+    flags: uint,
+}
+
+pub struct Info {
+    address: SocketAddr,
+    family: uint,
+    socktype: Option<SocketType>,
+    protocol: Option<Protocol>,
+    flags: uint,
+}
+
+/// Easy name resolution. Given a hostname, returns the list of IP addresses for
+/// that hostname.
+///
+/// # Failure
+///
+/// On failure, this will raise on the `io_error` condition.
+pub fn get_host_addresses(host: &str) -> Option<~[IpAddr]> {
+    lookup(Some(host), None, None).map(|a| a.map(|i| i.address.ip))
+}
+
+/// Full-fleged resolution. This function will perform a synchronous call to
+/// getaddrinfo, controlled by the parameters
+///
+/// # Arguments
+///
+/// * hostname - an optional hostname to lookup against
+/// * servname - an optional service name, listed in the system services
+/// * hint - see the hint structure, and "man -s 3 getaddrinfo", for how this
+///          controls lookup
+///
+/// # Failure
+///
+/// On failure, this will raise on the `io_error` condition.
+///
+/// XXX: this is not public because the `Hint` structure is not ready for public
+///      consumption just yet.
+fn lookup(hostname: Option<&str>, servname: Option<&str>,
+          hint: Option<Hint>) -> Option<~[Info]> {
+    do with_local_io |io| {
+        match io.get_host_addresses(hostname, servname, hint) {
+            Ok(i) => Some(i),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use option::Some;
+    use io::net::ip::Ipv4Addr;
+    use super::*;
+
+    #[test]
+    #[ignore(cfg(target_os="android"))] // cannot give tcp/ip permission without help of apk
+    fn dns_smoke_test() {
+        let ipaddrs = get_host_addresses("localhost").unwrap();
+        let mut found_local = false;
+        let local_addr = &Ipv4Addr(127, 0, 0, 1);
+        for addr in ipaddrs.iter() {
+            found_local = found_local || addr == local_addr;
+        }
+        assert!(found_local);
+    }
+}
diff --git a/src/libstd/io/net/ip.rs b/src/libstd/io/net/ip.rs
new file mode 100644
index 00000000000..07240a4a509
--- /dev/null
+++ b/src/libstd/io/net/ip.rs
@@ -0,0 +1,449 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use container::Container;
+use from_str::FromStr;
+use option::{Option, None, Some};
+use to_str::ToStr;
+use vec::{MutableCloneableVector, ImmutableVector};
+
+pub type Port = u16;
+
+#[deriving(Eq, TotalEq, Clone)]
+pub enum IpAddr {
+    Ipv4Addr(u8, u8, u8, u8),
+    Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16)
+}
+
+impl ToStr for IpAddr {
+    fn to_str(&self) -> ~str {
+        match *self {
+            Ipv4Addr(a, b, c, d) =>
+                format!("{}.{}.{}.{}", a, b, c, d),
+
+            // Ipv4 Compatible address
+            Ipv6Addr(0, 0, 0, 0, 0, 0, g, h) => {
+                format!("::{}.{}.{}.{}", (g >> 8) as u8, g as u8,
+                        (h >> 8) as u8, h as u8)
+            }
+
+            // Ipv4-Mapped address
+            Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, g, h) => {
+                format!("::FFFF:{}.{}.{}.{}", (g >> 8) as u8, g as u8,
+                        (h >> 8) as u8, h as u8)
+            }
+
+            Ipv6Addr(a, b, c, d, e, f, g, h) =>
+                format!("{}:{}:{}:{}:{}:{}:{}:{}", a, b, c, d, e, f, g, h)
+        }
+    }
+}
+
+#[deriving(Eq, TotalEq, Clone)]
+pub struct SocketAddr {
+    ip: IpAddr,
+    port: Port,
+}
+
+
+impl ToStr for SocketAddr {
+    fn to_str(&self) -> ~str {
+        match self.ip {
+            Ipv4Addr(*) => format!("{}:{}", self.ip.to_str(), self.port),
+            Ipv6Addr(*) => format!("[{}]:{}", self.ip.to_str(), self.port),
+        }
+    }
+}
+
+struct Parser<'self> {
+    // parsing as ASCII, so can use byte array
+    s: &'self [u8],
+    pos: uint,
+}
+
+impl<'self> Parser<'self> {
+    fn new(s: &'self str) -> Parser<'self> {
+        Parser {
+            s: s.as_bytes(),
+            pos: 0,
+        }
+    }
+
+    fn is_eof(&self) -> bool {
+        self.pos == self.s.len()
+    }
+
+    // Commit only if parser returns Some
+    fn read_atomically<T>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> Option<T> {
+        let pos = self.pos;
+        let r = cb(self);
+        if r.is_none() {
+            self.pos = pos;
+        }
+        r
+    }
+
+    // Commit only if parser read till EOF
+    fn read_till_eof<T>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> Option<T> {
+        do self.read_atomically |p| {
+            cb(p).filtered(|_| p.is_eof())
+        }
+    }
+
+    // Return result of first successful parser
+    fn read_or<T>(&mut self, parsers: &[&fn(&mut Parser) -> Option<T>]) -> Option<T> {
+        for pf in parsers.iter() {
+            match self.read_atomically(|p: &mut Parser| (*pf)(p)) {
+                Some(r) => return Some(r),
+                None => {}
+            }
+        }
+        None
+    }
+
+    // Apply 3 parsers sequentially
+    fn read_seq_3<A, B, C>(&mut self,
+            pa: &fn(&mut Parser) -> Option<A>,
+            pb: &fn(&mut Parser) -> Option<B>,
+            pc: &fn(&mut Parser) -> Option<C>
+        ) -> Option<(A, B, C)>
+    {
+        do self.read_atomically |p| {
+            let a = pa(p);
+            let b = if a.is_some() { pb(p) } else { None };
+            let c = if b.is_some() { pc(p) } else { None };
+            match (a, b, c) {
+                (Some(a), Some(b), Some(c)) => Some((a, b, c)),
+                _ => None
+            }
+        }
+    }
+
+    // Read next char
+    fn read_char(&mut self) -> Option<char> {
+        if self.is_eof() {
+            None
+        } else {
+            let r = self.s[self.pos] as char;
+            self.pos += 1;
+            Some(r)
+        }
+    }
+
+    // Return char and advance iff next char is equal to requested
+    fn read_given_char(&mut self, c: char) -> Option<char> {
+        do self.read_atomically |p| {
+            p.read_char().filtered(|&next| next == c)
+        }
+    }
+
+    // Read digit
+    fn read_digit(&mut self, radix: u8) -> Option<u8> {
+        fn parse_digit(c: char, radix: u8) -> Option<u8> {
+            let c = c as u8;
+            // assuming radix is either 10 or 16
+            if c >= '0' as u8 && c <= '9' as u8 {
+                Some((c - '0' as u8) as u8)
+            } else if radix > 10 && c >= 'a' as u8 && c < 'a' as u8 + (radix - 10) {
+                Some((c - 'a' as u8 + 10) as u8)
+            } else if radix > 10 && c >= 'A' as u8 && c < 'A' as u8 + (radix - 10) {
+                Some((c - 'A' as u8 + 10) as u8)
+            } else {
+                None
+            }
+        }
+
+        do self.read_atomically |p| {
+            p.read_char().and_then(|c| parse_digit(c, radix))
+        }
+    }
+
+    fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
+        let mut r = 0u32;
+        let mut digit_count = 0;
+        loop {
+            match self.read_digit(radix) {
+                Some(d) => {
+                    r = r * (radix as u32) + (d as u32);
+                    digit_count += 1;
+                    if digit_count > max_digits || r >= upto {
+                        return None
+                    }
+                }
+                None => {
+                    if digit_count == 0 {
+                        return None
+                    } else {
+                        return Some(r)
+                    }
+                }
+            };
+        }
+    }
+
+    // Read number, failing if max_digits of number value exceeded
+    fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
+        do self.read_atomically |p| {
+            p.read_number_impl(radix, max_digits, upto)
+        }
+    }
+
+    fn read_ipv4_addr_impl(&mut self) -> Option<IpAddr> {
+        let mut bs = [0u8, ..4];
+        let mut i = 0;
+        while i < 4 {
+            if i != 0 && self.read_given_char('.').is_none() {
+                return None;
+            }
+
+            let octet = self.read_number(10, 3, 0x100).map(|n| n as u8);
+            match octet {
+                Some(d) => bs[i] = d,
+                None => return None,
+            };
+            i += 1;
+        }
+        Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3]))
+    }
+
+    // Read IPv4 address
+    fn read_ipv4_addr(&mut self) -> Option<IpAddr> {
+        do self.read_atomically |p| {
+            p.read_ipv4_addr_impl()
+        }
+    }
+
+    fn read_ipv6_addr_impl(&mut self) -> Option<IpAddr> {
+        fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr {
+            assert!(head.len() + tail.len() <= 8);
+            let mut gs = [0u16, ..8];
+            gs.copy_from(head);
+            gs.mut_slice(8 - tail.len(), 8).copy_from(tail);
+            Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7])
+        }
+
+        fn read_groups(p: &mut Parser, groups: &mut [u16, ..8], limit: uint) -> (uint, bool) {
+            let mut i = 0;
+            while i < limit {
+                if i < limit - 1 {
+                    let ipv4 = do p.read_atomically |p| {
+                        if i == 0 || p.read_given_char(':').is_some() {
+                            p.read_ipv4_addr()
+                        } else {
+                            None
+                        }
+                    };
+                    match ipv4 {
+                        Some(Ipv4Addr(a, b, c, d)) => {
+                            groups[i + 0] = (a as u16 << 8) | (b as u16);
+                            groups[i + 1] = (c as u16 << 8) | (d as u16);
+                            return (i + 2, true);
+                        }
+                        _ => {}
+                    }
+                }
+
+                let group = do p.read_atomically |p| {
+                    if i == 0 || p.read_given_char(':').is_some() {
+                        p.read_number(16, 4, 0x10000).map(|n| n as u16)
+                    } else {
+                        None
+                    }
+                };
+                match group {
+                    Some(g) => groups[i] = g,
+                    None => return (i, false)
+                }
+                i += 1;
+            }
+            (i, false)
+        }
+
+        let mut head = [0u16, ..8];
+        let (head_size, head_ipv4) = read_groups(self, &mut head, 8);
+
+        if head_size == 8 {
+            return Some(Ipv6Addr(
+                head[0], head[1], head[2], head[3],
+                head[4], head[5], head[6], head[7]))
+        }
+
+        // IPv4 part is not allowed before `::`
+        if head_ipv4 {
+            return None
+        }
+
+        // read `::` if previous code parsed less than 8 groups
+        if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() {
+            return None;
+        }
+
+        let mut tail = [0u16, ..8];
+        let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size);
+        Some(ipv6_addr_from_head_tail(head.slice(0, head_size), tail.slice(0, tail_size)))
+    }
+
+    fn read_ipv6_addr(&mut self) -> Option<IpAddr> {
+        do self.read_atomically |p| {
+            p.read_ipv6_addr_impl()
+        }
+    }
+
+    fn read_ip_addr(&mut self) -> Option<IpAddr> {
+        let ipv4_addr = |p: &mut Parser| p.read_ipv4_addr();
+        let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr();
+        self.read_or([ipv4_addr, ipv6_addr])
+    }
+
+    fn read_socket_addr(&mut self) -> Option<SocketAddr> {
+        let ip_addr = |p: &mut Parser| {
+            let ipv4_p = |p: &mut Parser| p.read_ip_addr();
+            let ipv6_p = |p: &mut Parser| {
+                let open_br = |p: &mut Parser| p.read_given_char('[');
+                let ip_addr = |p: &mut Parser| p.read_ipv6_addr();
+                let clos_br = |p: &mut Parser| p.read_given_char(']');
+                p.read_seq_3::<char, IpAddr, char>(open_br, ip_addr, clos_br)
+                        .map(|t| match t { (_, ip, _) => ip })
+            };
+            p.read_or([ipv4_p, ipv6_p])
+        };
+        let colon = |p: &mut Parser| p.read_given_char(':');
+        let port  = |p: &mut Parser| p.read_number(10, 5, 0x10000).map(|n| n as u16);
+
+        // host, colon, port
+        self.read_seq_3::<IpAddr, char, u16>(ip_addr, colon, port)
+                .map(|t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } })
+    }
+}
+
+impl FromStr for IpAddr {
+    fn from_str(s: &str) -> Option<IpAddr> {
+        do Parser::new(s).read_till_eof |p| {
+            p.read_ip_addr()
+        }
+    }
+}
+
+impl FromStr for SocketAddr {
+    fn from_str(s: &str) -> Option<SocketAddr> {
+        do Parser::new(s).read_till_eof |p| {
+            p.read_socket_addr()
+        }
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use from_str::FromStr;
+    use option::{Option, Some, None};
+
+    #[test]
+    fn test_from_str_ipv4() {
+        assert_eq!(Some(Ipv4Addr(127, 0, 0, 1)), FromStr::from_str("127.0.0.1"));
+        assert_eq!(Some(Ipv4Addr(255, 255, 255, 255)), FromStr::from_str("255.255.255.255"));
+        assert_eq!(Some(Ipv4Addr(0, 0, 0, 0)), FromStr::from_str("0.0.0.0"));
+
+        // out of range
+        let none: Option<IpAddr> = FromStr::from_str("256.0.0.1");
+        assert_eq!(None, none);
+        // too short
+        let none: Option<IpAddr> = FromStr::from_str("255.0.0");
+        assert_eq!(None, none);
+        // too long
+        let none: Option<IpAddr> = FromStr::from_str("255.0.0.1.2");
+        assert_eq!(None, none);
+        // no number between dots
+        let none: Option<IpAddr> = FromStr::from_str("255.0..1");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn test_from_str_ipv6() {
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("0:0:0:0:0:0:0:0"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("0:0:0:0:0:0:0:1"));
+
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("::1"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("::"));
+
+        assert_eq!(Some(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)),
+                FromStr::from_str("2a02:6b8::11:11"));
+
+        // too long group
+        let none: Option<IpAddr> = FromStr::from_str("::00000");
+        assert_eq!(None, none);
+        // too short
+        let none: Option<IpAddr> = FromStr::from_str("1:2:3:4:5:6:7");
+        assert_eq!(None, none);
+        // too long
+        let none: Option<IpAddr> = FromStr::from_str("1:2:3:4:5:6:7:8:9");
+        assert_eq!(None, none);
+        // triple colon
+        let none: Option<IpAddr> = FromStr::from_str("1:2:::6:7:8");
+        assert_eq!(None, none);
+        // two double colons
+        let none: Option<IpAddr> = FromStr::from_str("1:2::6::8");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn test_from_str_ipv4_in_ipv6() {
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)),
+                FromStr::from_str("::192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)),
+                FromStr::from_str("::FFFF:192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
+                FromStr::from_str("64:ff9b::192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
+                FromStr::from_str("2001:db8:122:c000:2:2100:192.0.2.33"));
+
+        // colon after v4
+        let none: Option<IpAddr> = FromStr::from_str("::127.0.0.1:");
+        assert_eq!(None, none);
+        // not enought groups
+        let none: Option<IpAddr> = FromStr::from_str("1.2.3.4.5:127.0.0.1");
+        assert_eq!(None, none);
+        // too many groups
+        let none: Option<IpAddr> =
+            FromStr::from_str("1.2.3.4.5:6:7:127.0.0.1");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn test_from_str_socket_addr() {
+        assert_eq!(Some(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }),
+                FromStr::from_str("77.88.21.11:80"));
+        assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }),
+                FromStr::from_str("[2a02:6b8:0:1::1]:53"));
+        assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }),
+                FromStr::from_str("[::127.0.0.1]:22"));
+
+        // without port
+        let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1");
+        assert_eq!(None, none);
+        // without port
+        let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1:");
+        assert_eq!(None, none);
+        // wrong brackets around v4
+        let none: Option<SocketAddr> = FromStr::from_str("[127.0.0.1]:22");
+        assert_eq!(None, none);
+        // port out of range
+        let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1:123456");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn ipv6_addr_to_str() {
+        let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
+        assert!(a1.to_str() == ~"::ffff:192.0.2.128" || a1.to_str() == ~"::FFFF:192.0.2.128");
+    }
+
+}
diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs
new file mode 100644
index 00000000000..cf109167089
--- /dev/null
+++ b/src/libstd/io/net/mod.rs
@@ -0,0 +1,18 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub use self::addrinfo::get_host_addresses;
+
+pub mod addrinfo;
+pub mod tcp;
+pub mod udp;
+pub mod ip;
+#[cfg(unix)]
+pub mod unix;
diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs
new file mode 100644
index 00000000000..a59d652d739
--- /dev/null
+++ b/src/libstd/io/net/tcp.rs
@@ -0,0 +1,725 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use option::{Option, Some, None};
+use result::{Ok, Err};
+use io::net::ip::SocketAddr;
+use io::{Reader, Writer, Listener, Acceptor};
+use io::{io_error, EndOfFile};
+use rt::rtio::{IoFactory, with_local_io,
+               RtioSocket, RtioTcpListener, RtioTcpAcceptor, RtioTcpStream};
+
+pub struct TcpStream {
+    priv obj: ~RtioTcpStream
+}
+
+impl TcpStream {
+    fn new(s: ~RtioTcpStream) -> TcpStream {
+        TcpStream { obj: s }
+    }
+
+    pub fn connect(addr: SocketAddr) -> Option<TcpStream> {
+        do with_local_io |io| {
+            match io.tcp_connect(addr) {
+                Ok(s) => Some(TcpStream::new(s)),
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }
+    }
+
+    pub fn peer_name(&mut self) -> Option<SocketAddr> {
+        match self.obj.peer_name() {
+            Ok(pn) => Some(pn),
+            Err(ioerr) => {
+                debug!("failed to get peer name: {:?}", ioerr);
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+
+    pub fn socket_name(&mut self) -> Option<SocketAddr> {
+        match self.obj.socket_name() {
+            Ok(sn) => Some(sn),
+            Err(ioerr) => {
+                debug!("failed to get socket name: {:?}", ioerr);
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+impl Reader for TcpStream {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        match self.obj.read(buf) {
+            Ok(read) => Some(read),
+            Err(ioerr) => {
+                // EOF is indicated by returning None
+                if ioerr.kind != EndOfFile {
+                    io_error::cond.raise(ioerr);
+                }
+                return None;
+            }
+        }
+    }
+
+    fn eof(&mut self) -> bool { fail!() }
+}
+
+impl Writer for TcpStream {
+    fn write(&mut self, buf: &[u8]) {
+        match self.obj.write(buf) {
+            Ok(_) => (),
+            Err(ioerr) => io_error::cond.raise(ioerr),
+        }
+    }
+}
+
+pub struct TcpListener {
+    priv obj: ~RtioTcpListener
+}
+
+impl TcpListener {
+    pub fn bind(addr: SocketAddr) -> Option<TcpListener> {
+        do with_local_io |io| {
+            match io.tcp_bind(addr) {
+                Ok(l) => Some(TcpListener { obj: l }),
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }
+    }
+
+    pub fn socket_name(&mut self) -> Option<SocketAddr> {
+        match self.obj.socket_name() {
+            Ok(sn) => Some(sn),
+            Err(ioerr) => {
+                debug!("failed to get socket name: {:?}", ioerr);
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+impl Listener<TcpStream, TcpAcceptor> for TcpListener {
+    fn listen(self) -> Option<TcpAcceptor> {
+        match self.obj.listen() {
+            Ok(acceptor) => Some(TcpAcceptor { obj: acceptor }),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+pub struct TcpAcceptor {
+    priv obj: ~RtioTcpAcceptor
+}
+
+impl Acceptor<TcpStream> for TcpAcceptor {
+    fn accept(&mut self) -> Option<TcpStream> {
+        match self.obj.accept() {
+            Ok(s) => Some(TcpStream::new(s)),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use cell::Cell;
+    use rt::test::*;
+    use io::net::ip::{Ipv4Addr, SocketAddr};
+    use io::*;
+    use prelude::*;
+    use rt::comm::oneshot;
+
+    #[test] #[ignore]
+    fn bind_error() {
+        do run_in_mt_newsched_task {
+            let mut called = false;
+            do io_error::cond.trap(|e| {
+                assert!(e.kind == PermissionDenied);
+                called = true;
+            }).inside {
+                let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 };
+                let listener = TcpListener::bind(addr);
+                assert!(listener.is_none());
+            }
+            assert!(called);
+        }
+    }
+
+    #[test]
+    fn connect_error() {
+        do run_in_mt_newsched_task {
+            let mut called = false;
+            do io_error::cond.trap(|e| {
+                let expected_error = if cfg!(unix) {
+                    ConnectionRefused
+                } else {
+                    // On Win32, opening port 1 gives WSAEADDRNOTAVAIL error.
+                    OtherIoError
+                };
+                assert_eq!(e.kind, expected_error);
+                called = true;
+            }).inside {
+                let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 };
+                let stream = TcpStream::connect(addr);
+                assert!(stream.is_none());
+            }
+            assert!(called);
+        }
+    }
+
+    #[test]
+    fn smoke_test_ip4() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip4();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let mut buf = [0];
+                stream.read(buf);
+                assert!(buf[0] == 99);
+            }
+
+            do spawntask {
+                port.take().recv();
+                let mut stream = TcpStream::connect(addr);
+                stream.write([99]);
+            }
+        }
+    }
+
+    #[test]
+    fn smoke_test_ip6() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip6();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let mut buf = [0];
+                stream.read(buf);
+                assert!(buf[0] == 99);
+            }
+
+            do spawntask {
+                port.take().recv();
+                let mut stream = TcpStream::connect(addr);
+                stream.write([99]);
+            }
+        }
+    }
+
+    #[test]
+    fn read_eof_ip4() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip4();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let mut buf = [0];
+                let nread = stream.read(buf);
+                assert!(nread.is_none());
+            }
+
+            do spawntask {
+                port.take().recv();
+                let _stream = TcpStream::connect(addr);
+                // Close
+            }
+        }
+    }
+
+    #[test]
+    fn read_eof_ip6() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip6();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let mut buf = [0];
+                let nread = stream.read(buf);
+                assert!(nread.is_none());
+            }
+
+            do spawntask {
+                port.take().recv();
+                let _stream = TcpStream::connect(addr);
+                // Close
+            }
+        }
+    }
+
+    #[test]
+    fn read_eof_twice_ip4() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip4();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let mut buf = [0];
+                let nread = stream.read(buf);
+                assert!(nread.is_none());
+                do io_error::cond.trap(|e| {
+                    if cfg!(windows) {
+                        assert_eq!(e.kind, NotConnected);
+                    } else {
+                        fail!();
+                    }
+                }).inside {
+                    let nread = stream.read(buf);
+                    assert!(nread.is_none());
+                }
+            }
+
+            do spawntask {
+                port.take().recv();
+                let _stream = TcpStream::connect(addr);
+                // Close
+            }
+        }
+    }
+
+    #[test]
+    fn read_eof_twice_ip6() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip6();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let mut buf = [0];
+                let nread = stream.read(buf);
+                assert!(nread.is_none());
+                do io_error::cond.trap(|e| {
+                    if cfg!(windows) {
+                        assert_eq!(e.kind, NotConnected);
+                    } else {
+                        fail!();
+                    }
+                }).inside {
+                    let nread = stream.read(buf);
+                    assert!(nread.is_none());
+                }
+            }
+
+            do spawntask {
+                port.take().recv();
+                let _stream = TcpStream::connect(addr);
+                // Close
+            }
+        }
+    }
+
+    #[test]
+    fn write_close_ip4() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip4();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let buf = [0];
+                loop {
+                    let mut stop = false;
+                    do io_error::cond.trap(|e| {
+                        // NB: ECONNRESET on linux, EPIPE on mac, ECONNABORTED
+                        //     on windows
+                        assert!(e.kind == ConnectionReset ||
+                                e.kind == BrokenPipe ||
+                                e.kind == ConnectionAborted,
+                                "unknown error: {:?}", e);
+                        stop = true;
+                    }).inside {
+                        stream.write(buf);
+                    }
+                    if stop { break }
+                }
+            }
+
+            do spawntask {
+                port.take().recv();
+                let _stream = TcpStream::connect(addr);
+                // Close
+            }
+        }
+    }
+
+    #[test]
+    fn write_close_ip6() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip6();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                let mut stream = acceptor.accept();
+                let buf = [0];
+                loop {
+                    let mut stop = false;
+                    do io_error::cond.trap(|e| {
+                        // NB: ECONNRESET on linux, EPIPE on mac, ECONNABORTED
+                        //     on windows
+                        assert!(e.kind == ConnectionReset ||
+                                e.kind == BrokenPipe ||
+                                e.kind == ConnectionAborted,
+                                "unknown error: {:?}", e);
+                        stop = true;
+                    }).inside {
+                        stream.write(buf);
+                    }
+                    if stop { break }
+                }
+            }
+
+            do spawntask {
+                port.take().recv();
+                let _stream = TcpStream::connect(addr);
+                // Close
+            }
+        }
+    }
+
+    #[test]
+    fn multiple_connect_serial_ip4() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip4();
+            let max = 10;
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                for ref mut stream in acceptor.incoming().take(max) {
+                    let mut buf = [0];
+                    stream.read(buf);
+                    assert_eq!(buf[0], 99);
+                }
+            }
+
+            do spawntask {
+                port.take().recv();
+                do max.times {
+                    let mut stream = TcpStream::connect(addr);
+                    stream.write([99]);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn multiple_connect_serial_ip6() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip6();
+            let max = 10;
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                for ref mut stream in acceptor.incoming().take(max) {
+                    let mut buf = [0];
+                    stream.read(buf);
+                    assert_eq!(buf[0], 99);
+                }
+            }
+
+            do spawntask {
+                port.take().recv();
+                do max.times {
+                    let mut stream = TcpStream::connect(addr);
+                    stream.write([99]);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn multiple_connect_interleaved_greedy_schedule_ip4() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip4();
+            static MAX: int = 10;
+            let (port, chan) = oneshot();
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) {
+                    let stream = Cell::new(stream);
+                    // Start another task to handle the connection
+                    do spawntask {
+                        let mut stream = stream.take();
+                        let mut buf = [0];
+                        stream.read(buf);
+                        assert!(buf[0] == i as u8);
+                        debug!("read");
+                    }
+                }
+            }
+
+            port.recv();
+            connect(0, addr);
+
+            fn connect(i: int, addr: SocketAddr) {
+                if i == MAX { return }
+
+                do spawntask {
+                    debug!("connecting");
+                    let mut stream = TcpStream::connect(addr);
+                    // Connect again before writing
+                    connect(i + 1, addr);
+                    debug!("writing");
+                    stream.write([i as u8]);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn multiple_connect_interleaved_greedy_schedule_ip6() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip6();
+            static MAX: int = 10;
+            let (port, chan) = oneshot();
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) {
+                    let stream = Cell::new(stream);
+                    // Start another task to handle the connection
+                    do spawntask {
+                        let mut stream = stream.take();
+                        let mut buf = [0];
+                        stream.read(buf);
+                        assert!(buf[0] == i as u8);
+                        debug!("read");
+                    }
+                }
+            }
+
+            port.recv();
+            connect(0, addr);
+
+            fn connect(i: int, addr: SocketAddr) {
+                if i == MAX { return }
+
+                do spawntask {
+                    debug!("connecting");
+                    let mut stream = TcpStream::connect(addr);
+                    // Connect again before writing
+                    connect(i + 1, addr);
+                    debug!("writing");
+                    stream.write([i as u8]);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn multiple_connect_interleaved_lazy_schedule_ip4() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip4();
+            static MAX: int = 10;
+            let (port, chan) = oneshot();
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                for stream in acceptor.incoming().take(MAX as uint) {
+                    let stream = Cell::new(stream);
+                    // Start another task to handle the connection
+                    do spawntask_later {
+                        let mut stream = stream.take();
+                        let mut buf = [0];
+                        stream.read(buf);
+                        assert!(buf[0] == 99);
+                        debug!("read");
+                    }
+                }
+            }
+
+            port.recv();
+            connect(0, addr);
+
+            fn connect(i: int, addr: SocketAddr) {
+                if i == MAX { return }
+
+                do spawntask_later {
+                    debug!("connecting");
+                    let mut stream = TcpStream::connect(addr);
+                    // Connect again before writing
+                    connect(i + 1, addr);
+                    debug!("writing");
+                    stream.write([99]);
+                }
+            }
+        }
+    }
+    #[test]
+    fn multiple_connect_interleaved_lazy_schedule_ip6() {
+        do run_in_mt_newsched_task {
+            let addr = next_test_ip6();
+            static MAX: int = 10;
+            let (port, chan) = oneshot();
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+                for stream in acceptor.incoming().take(MAX as uint) {
+                    let stream = Cell::new(stream);
+                    // Start another task to handle the connection
+                    do spawntask_later {
+                        let mut stream = stream.take();
+                        let mut buf = [0];
+                        stream.read(buf);
+                        assert!(buf[0] == 99);
+                        debug!("read");
+                    }
+                }
+            }
+
+            port.recv();
+            connect(0, addr);
+
+            fn connect(i: int, addr: SocketAddr) {
+                if i == MAX { return }
+
+                do spawntask_later {
+                    debug!("connecting");
+                    let mut stream = TcpStream::connect(addr);
+                    // Connect again before writing
+                    connect(i + 1, addr);
+                    debug!("writing");
+                    stream.write([99]);
+                }
+            }
+        }
+    }
+
+    #[cfg(test)]
+    fn socket_name(addr: SocketAddr) {
+        do run_in_mt_newsched_task {
+            do spawntask {
+                let mut listener = TcpListener::bind(addr).unwrap();
+
+                // Make sure socket_name gives
+                // us the socket we binded to.
+                let so_name = listener.socket_name();
+                assert!(so_name.is_some());
+                assert_eq!(addr, so_name.unwrap());
+
+            }
+        }
+    }
+
+    #[cfg(test)]
+    fn peer_name(addr: SocketAddr) {
+        do run_in_mt_newsched_task {
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = TcpListener::bind(addr).listen();
+                chan.take().send(());
+
+                acceptor.accept();
+            }
+
+            do spawntask {
+                port.take().recv();
+                let stream = TcpStream::connect(addr);
+
+                assert!(stream.is_some());
+                let mut stream = stream.unwrap();
+
+                // Make sure peer_name gives us the
+                // address/port of the peer we've
+                // connected to.
+                let peer_name = stream.peer_name();
+                assert!(peer_name.is_some());
+                assert_eq!(addr, peer_name.unwrap());
+            }
+        }
+    }
+
+    #[test]
+    fn socket_and_peer_name_ip4() {
+        peer_name(next_test_ip4());
+        socket_name(next_test_ip4());
+    }
+
+    #[test]
+    fn socket_and_peer_name_ip6() {
+        // XXX: peer name is not consistent
+        //peer_name(next_test_ip6());
+        socket_name(next_test_ip6());
+    }
+
+}
diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs
new file mode 100644
index 00000000000..1a2245ac442
--- /dev/null
+++ b/src/libstd/io/net/udp.rs
@@ -0,0 +1,321 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use option::{Option, Some, None};
+use result::{Ok, Err};
+use io::net::ip::SocketAddr;
+use io::{Reader, Writer};
+use io::{io_error, EndOfFile};
+use rt::rtio::{RtioSocket, RtioUdpSocket, IoFactory, with_local_io};
+
+pub struct UdpSocket {
+    priv obj: ~RtioUdpSocket
+}
+
+impl UdpSocket {
+    pub fn bind(addr: SocketAddr) -> Option<UdpSocket> {
+        do with_local_io |io| {
+            match io.udp_bind(addr) {
+                Ok(s) => Some(UdpSocket { obj: s }),
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }
+    }
+
+    pub fn recvfrom(&mut self, buf: &mut [u8]) -> Option<(uint, SocketAddr)> {
+        match self.obj.recvfrom(buf) {
+            Ok((nread, src)) => Some((nread, src)),
+            Err(ioerr) => {
+                // EOF is indicated by returning None
+                if ioerr.kind != EndOfFile {
+                    io_error::cond.raise(ioerr);
+                }
+                None
+            }
+        }
+    }
+
+    pub fn sendto(&mut self, buf: &[u8], dst: SocketAddr) {
+        match self.obj.sendto(buf, dst) {
+            Ok(_) => (),
+            Err(ioerr) => io_error::cond.raise(ioerr),
+        }
+    }
+
+    pub fn connect(self, other: SocketAddr) -> UdpStream {
+        UdpStream { socket: self, connectedTo: other }
+    }
+
+    pub fn socket_name(&mut self) -> Option<SocketAddr> {
+        match self.obj.socket_name() {
+            Ok(sn) => Some(sn),
+            Err(ioerr) => {
+                debug!("failed to get socket name: {:?}", ioerr);
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+pub struct UdpStream {
+    priv socket: UdpSocket,
+    priv connectedTo: SocketAddr
+}
+
+impl UdpStream {
+    pub fn as_socket<T>(&mut self, f: &fn(&mut UdpSocket) -> T) -> T { f(&mut self.socket) }
+
+    pub fn disconnect(self) -> UdpSocket { self.socket }
+}
+
+impl Reader for UdpStream {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        let peer = self.connectedTo;
+        do self.as_socket |sock| {
+            match sock.recvfrom(buf) {
+                Some((_nread, src)) if src != peer => Some(0),
+                Some((nread, _src)) => Some(nread),
+                None => None,
+            }
+        }
+    }
+
+    fn eof(&mut self) -> bool { fail!() }
+}
+
+impl Writer for UdpStream {
+    fn write(&mut self, buf: &[u8]) {
+        do self.as_socket |sock| {
+            sock.sendto(buf, self.connectedTo);
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use rt::test::*;
+    use io::net::ip::{Ipv4Addr, SocketAddr};
+    use io::*;
+    use option::{Some, None};
+    use rt::comm::oneshot;
+    use cell::Cell;
+
+    #[test]  #[ignore]
+    fn bind_error() {
+        do run_in_mt_newsched_task {
+            let mut called = false;
+            do io_error::cond.trap(|e| {
+                assert!(e.kind == PermissionDenied);
+                called = true;
+            }).inside {
+                let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 };
+                let socket = UdpSocket::bind(addr);
+                assert!(socket.is_none());
+            }
+            assert!(called);
+        }
+    }
+
+    #[test]
+    fn socket_smoke_test_ip4() {
+        do run_in_mt_newsched_task {
+            let server_ip = next_test_ip4();
+            let client_ip = next_test_ip4();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                match UdpSocket::bind(server_ip) {
+                    Some(ref mut server) => {
+                        chan.take().send(());
+                        let mut buf = [0];
+                        match server.recvfrom(buf) {
+                            Some((nread, src)) => {
+                                assert_eq!(nread, 1);
+                                assert_eq!(buf[0], 99);
+                                assert_eq!(src, client_ip);
+                            }
+                            None => fail!()
+                        }
+                    }
+                    None => fail!()
+                }
+            }
+
+            do spawntask {
+                match UdpSocket::bind(client_ip) {
+                    Some(ref mut client) => {
+                        port.take().recv();
+                        client.sendto([99], server_ip)
+                    }
+                    None => fail!()
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn socket_smoke_test_ip6() {
+        do run_in_mt_newsched_task {
+            let server_ip = next_test_ip6();
+            let client_ip = next_test_ip6();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                match UdpSocket::bind(server_ip) {
+                    Some(ref mut server) => {
+                        chan.take().send(());
+                        let mut buf = [0];
+                        match server.recvfrom(buf) {
+                            Some((nread, src)) => {
+                                assert_eq!(nread, 1);
+                                assert_eq!(buf[0], 99);
+                                assert_eq!(src, client_ip);
+                            }
+                            None => fail!()
+                        }
+                    }
+                    None => fail!()
+                }
+            }
+
+            do spawntask {
+                match UdpSocket::bind(client_ip) {
+                    Some(ref mut client) => {
+                        port.take().recv();
+                        client.sendto([99], server_ip)
+                    }
+                    None => fail!()
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn stream_smoke_test_ip4() {
+        do run_in_mt_newsched_task {
+            let server_ip = next_test_ip4();
+            let client_ip = next_test_ip4();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                match UdpSocket::bind(server_ip) {
+                    Some(server) => {
+                        let server = ~server;
+                        let mut stream = server.connect(client_ip);
+                        chan.take().send(());
+                        let mut buf = [0];
+                        match stream.read(buf) {
+                            Some(nread) => {
+                                assert_eq!(nread, 1);
+                                assert_eq!(buf[0], 99);
+                            }
+                            None => fail!()
+                        }
+                    }
+                    None => fail!()
+                }
+            }
+
+            do spawntask {
+                match UdpSocket::bind(client_ip) {
+                    Some(client) => {
+                        let client = ~client;
+                        let mut stream = client.connect(server_ip);
+                        port.take().recv();
+                        stream.write([99]);
+                    }
+                    None => fail!()
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn stream_smoke_test_ip6() {
+        do run_in_mt_newsched_task {
+            let server_ip = next_test_ip6();
+            let client_ip = next_test_ip6();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                match UdpSocket::bind(server_ip) {
+                    Some(server) => {
+                        let server = ~server;
+                        let mut stream = server.connect(client_ip);
+                        chan.take().send(());
+                        let mut buf = [0];
+                        match stream.read(buf) {
+                            Some(nread) => {
+                                assert_eq!(nread, 1);
+                                assert_eq!(buf[0], 99);
+                            }
+                            None => fail!()
+                        }
+                    }
+                    None => fail!()
+                }
+            }
+
+            do spawntask {
+                match UdpSocket::bind(client_ip) {
+                    Some(client) => {
+                        let client = ~client;
+                        let mut stream = client.connect(server_ip);
+                        port.take().recv();
+                        stream.write([99]);
+                    }
+                    None => fail!()
+                }
+            }
+        }
+    }
+
+    #[cfg(test)]
+    fn socket_name(addr: SocketAddr) {
+        do run_in_mt_newsched_task {
+            do spawntask {
+                let server = UdpSocket::bind(addr);
+
+                assert!(server.is_some());
+                let mut server = server.unwrap();
+
+                // Make sure socket_name gives
+                // us the socket we binded to.
+                let so_name = server.socket_name();
+                assert!(so_name.is_some());
+                assert_eq!(addr, so_name.unwrap());
+
+            }
+        }
+    }
+
+    #[test]
+    fn socket_name_ip4() {
+        socket_name(next_test_ip4());
+    }
+
+    #[test]
+    fn socket_name_ip6() {
+        socket_name(next_test_ip6());
+    }
+}
diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs
new file mode 100644
index 00000000000..c6b4a2f2a42
--- /dev/null
+++ b/src/libstd/io/net/unix.rs
@@ -0,0 +1,295 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+Named pipes
+
+This module contains the ability to communicate over named pipes with
+synchronous I/O. On windows, this corresponds to talking over a Named Pipe,
+while on Unix it corresponds to UNIX domain sockets.
+
+These pipes are similar to TCP in the sense that you can have both a stream to a
+server and a server itself. The server provided accepts other `UnixStream`
+instances as clients.
+
+*/
+
+use prelude::*;
+
+use c_str::ToCStr;
+use rt::rtio::{IoFactory, RtioUnixListener, with_local_io};
+use rt::rtio::{RtioUnixAcceptor, RtioPipe};
+use io::pipe::PipeStream;
+use io::{io_error, Listener, Acceptor, Reader, Writer};
+
+/// A stream which communicates over a named pipe.
+pub struct UnixStream {
+    priv obj: PipeStream,
+}
+
+impl UnixStream {
+    fn new(obj: ~RtioPipe) -> UnixStream {
+        UnixStream { obj: PipeStream::new(obj) }
+    }
+
+    /// Connect to a pipe named by `path`. This will attempt to open a
+    /// connection to the underlying socket.
+    ///
+    /// The returned stream will be closed when the object falls out of scope.
+    ///
+    /// # Failure
+    ///
+    /// This function will raise on the `io_error` condition if the connection
+    /// could not be made.
+    ///
+    /// # Example
+    ///
+    ///     use std::io::net::unix::UnixStream;
+    ///
+    ///     let server = Path("path/to/my/socket");
+    ///     let mut stream = UnixStream::connect(&server);
+    ///     stream.write([1, 2, 3]);
+    ///
+    pub fn connect<P: ToCStr>(path: &P) -> Option<UnixStream> {
+        do with_local_io |io| {
+            match io.unix_connect(&path.to_c_str()) {
+                Ok(s) => Some(UnixStream::new(s)),
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }
+    }
+}
+
+impl Reader for UnixStream {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.obj.read(buf) }
+    fn eof(&mut self) -> bool { self.obj.eof() }
+}
+
+impl Writer for UnixStream {
+    fn write(&mut self, buf: &[u8]) { self.obj.write(buf) }
+}
+
+pub struct UnixListener {
+    priv obj: ~RtioUnixListener,
+}
+
+impl UnixListener {
+
+    /// Creates a new listener, ready to receive incoming connections on the
+    /// specified socket. The server will be named by `path`.
+    ///
+    /// This listener will be closed when it falls out of scope.
+    ///
+    /// # Failure
+    ///
+    /// This function will raise on the `io_error` condition if the specified
+    /// path could not be bound.
+    ///
+    /// # Example
+    ///
+    ///     use std::io::net::unix::UnixListener;
+    ///
+    ///     let server = Path("path/to/my/socket");
+    ///     let mut stream = UnixListener::bind(&server);
+    ///     for client in stream.incoming() {
+    ///         let mut client = client;
+    ///         client.write([1, 2, 3, 4]);
+    ///     }
+    ///
+    pub fn bind<P: ToCStr>(path: &P) -> Option<UnixListener> {
+        do with_local_io |io| {
+            match io.unix_bind(&path.to_c_str()) {
+                Ok(s) => Some(UnixListener{ obj: s }),
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }
+    }
+}
+
+impl Listener<UnixStream, UnixAcceptor> for UnixListener {
+    fn listen(self) -> Option<UnixAcceptor> {
+        match self.obj.listen() {
+            Ok(acceptor) => Some(UnixAcceptor { obj: acceptor }),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+pub struct UnixAcceptor {
+    priv obj: ~RtioUnixAcceptor,
+}
+
+impl Acceptor<UnixStream> for UnixAcceptor {
+    fn accept(&mut self) -> Option<UnixStream> {
+        match self.obj.accept() {
+            Ok(s) => Some(UnixStream::new(s)),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+                None
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::*;
+    use super::*;
+    use cell::Cell;
+    use rt::test::*;
+    use io::*;
+    use rt::comm::oneshot;
+
+    fn smalltest(server: ~fn(UnixStream), client: ~fn(UnixStream)) {
+        let server = Cell::new(server);
+        let client = Cell::new(client);
+        do run_in_mt_newsched_task {
+            let server = Cell::new(server.take());
+            let client = Cell::new(client.take());
+            let path1 = next_test_unix();
+            let path2 = path1.clone();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = UnixListener::bind(&path1).listen();
+                chan.take().send(());
+                server.take()(acceptor.accept().unwrap());
+            }
+
+            do spawntask {
+                port.take().recv();
+                client.take()(UnixStream::connect(&path2).unwrap());
+            }
+        }
+    }
+
+    #[test]
+    fn bind_error() {
+        do run_in_mt_newsched_task {
+            let mut called = false;
+            do io_error::cond.trap(|e| {
+                assert!(e.kind == PermissionDenied);
+                called = true;
+            }).inside {
+                let listener = UnixListener::bind(&("path/to/nowhere"));
+                assert!(listener.is_none());
+            }
+            assert!(called);
+        }
+    }
+
+    #[test]
+    fn connect_error() {
+        do run_in_mt_newsched_task {
+            let mut called = false;
+            do io_error::cond.trap(|e| {
+                assert_eq!(e.kind, OtherIoError);
+                called = true;
+            }).inside {
+                let stream = UnixStream::connect(&("path/to/nowhere"));
+                assert!(stream.is_none());
+            }
+            assert!(called);
+        }
+    }
+
+    #[test]
+    fn smoke() {
+        smalltest(|mut server| {
+            let mut buf = [0];
+            server.read(buf);
+            assert!(buf[0] == 99);
+        }, |mut client| {
+            client.write([99]);
+        })
+    }
+
+    #[test]
+    fn read_eof() {
+        smalltest(|mut server| {
+            let mut buf = [0];
+            assert!(server.read(buf).is_none());
+            assert!(server.read(buf).is_none());
+        }, |_client| {
+            // drop the client
+        })
+    }
+
+    #[test]
+    fn write_begone() {
+        smalltest(|mut server| {
+            let buf = [0];
+            let mut stop = false;
+            while !stop{
+                do io_error::cond.trap(|e| {
+                    assert!(e.kind == BrokenPipe || e.kind == NotConnected,
+                            "unknown error {:?}", e);
+                    stop = true;
+                }).inside {
+                    server.write(buf);
+                }
+            }
+        }, |_client| {
+            // drop the client
+        })
+    }
+
+    #[test]
+    fn accept_lots() {
+        do run_in_mt_newsched_task {
+            let times = 10;
+            let path1 = next_test_unix();
+            let path2 = path1.clone();
+            let (port, chan) = oneshot();
+            let port = Cell::new(port);
+            let chan = Cell::new(chan);
+
+            do spawntask {
+                let mut acceptor = UnixListener::bind(&path1).listen();
+                chan.take().send(());
+                do times.times {
+                    let mut client = acceptor.accept();
+                    let mut buf = [0];
+                    client.read(buf);
+                    assert_eq!(buf[0], 100);
+                }
+            }
+
+            do spawntask {
+                port.take().recv();
+                do times.times {
+                    let mut stream = UnixStream::connect(&path2);
+                    stream.write([100]);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn path_exists() {
+        do run_in_mt_newsched_task {
+            let path = next_test_unix();
+            let _acceptor = UnixListener::bind(&path).listen();
+            assert!(path.exists());
+        }
+    }
+}
diff --git a/src/libstd/io/option.rs b/src/libstd/io/option.rs
new file mode 100644
index 00000000000..5938252571f
--- /dev/null
+++ b/src/libstd/io/option.rs
@@ -0,0 +1,181 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementations of I/O traits for the Option type
+//!
+//! I/O constructors return option types to allow errors to be handled.
+//! These implementations allow e.g. `Option<File>` to be used
+//! as a `Reader` without unwrapping the option first.
+
+use option::*;
+use super::{Reader, Writer, Listener, Acceptor, Seek, SeekStyle};
+use super::{standard_error, PreviousIoError, io_error, IoError};
+
+fn prev_io_error() -> IoError {
+    standard_error(PreviousIoError)
+}
+
+impl<W: Writer> Writer for Option<W> {
+    fn write(&mut self, buf: &[u8]) {
+        match *self {
+            Some(ref mut writer) => writer.write(buf),
+            None => io_error::cond.raise(prev_io_error())
+        }
+    }
+
+    fn flush(&mut self) {
+        match *self {
+            Some(ref mut writer) => writer.flush(),
+            None => io_error::cond.raise(prev_io_error())
+        }
+    }
+}
+
+impl<R: Reader> Reader for Option<R> {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        match *self {
+            Some(ref mut reader) => reader.read(buf),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                None
+            }
+        }
+    }
+
+    fn eof(&mut self) -> bool {
+        match *self {
+            Some(ref mut reader) => reader.eof(),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                true
+            }
+        }
+    }
+}
+
+impl<S: Seek> Seek for Option<S> {
+    fn tell(&self) -> u64 {
+        match *self {
+            Some(ref seeker) => seeker.tell(),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                0
+            }
+        }
+    }
+    fn seek(&mut self, pos: i64, style: SeekStyle) {
+        match *self {
+            Some(ref mut seeker) => seeker.seek(pos, style),
+            None => io_error::cond.raise(prev_io_error())
+        }
+    }
+}
+
+impl<T, A: Acceptor<T>, L: Listener<T, A>> Listener<T, A> for Option<L> {
+    fn listen(self) -> Option<A> {
+        match self {
+            Some(listener) => listener.listen(),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                None
+            }
+        }
+    }
+}
+
+impl<T, A: Acceptor<T>> Acceptor<T> for Option<A> {
+    fn accept(&mut self) -> Option<T> {
+        match *self {
+            Some(ref mut acceptor) => acceptor.accept(),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                None
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use option::*;
+    use super::super::mem::*;
+    use rt::test::*;
+    use super::super::{PreviousIoError, io_error};
+
+    #[test]
+    fn test_option_writer() {
+        do run_in_mt_newsched_task {
+            let mut writer: Option<MemWriter> = Some(MemWriter::new());
+            writer.write([0, 1, 2]);
+            writer.flush();
+            assert_eq!(writer.unwrap().inner(), ~[0, 1, 2]);
+        }
+    }
+
+    #[test]
+    fn test_option_writer_error() {
+        do run_in_mt_newsched_task {
+            let mut writer: Option<MemWriter> = None;
+
+            let mut called = false;
+            do io_error::cond.trap(|err| {
+                assert_eq!(err.kind, PreviousIoError);
+                called = true;
+            }).inside {
+                writer.write([0, 0, 0]);
+            }
+            assert!(called);
+
+            let mut called = false;
+            do io_error::cond.trap(|err| {
+                assert_eq!(err.kind, PreviousIoError);
+                called = true;
+            }).inside {
+                writer.flush();
+            }
+            assert!(called);
+        }
+    }
+
+    #[test]
+    fn test_option_reader() {
+        do run_in_mt_newsched_task {
+            let mut reader: Option<MemReader> = Some(MemReader::new(~[0, 1, 2, 3]));
+            let mut buf = [0, 0];
+            reader.read(buf);
+            assert_eq!(buf, [0, 1]);
+            assert!(!reader.eof());
+        }
+    }
+
+    #[test]
+    fn test_option_reader_error() {
+        let mut reader: Option<MemReader> = None;
+        let mut buf = [];
+
+        let mut called = false;
+        do io_error::cond.trap(|err| {
+            assert_eq!(err.kind, PreviousIoError);
+            called = true;
+        }).inside {
+            reader.read(buf);
+        }
+        assert!(called);
+
+        let mut called = false;
+        do io_error::cond.trap(|err| {
+            assert_eq!(err.kind, PreviousIoError);
+            called = true;
+        }).inside {
+            assert!(reader.eof());
+        }
+        assert!(called);
+    }
+}
diff --git a/src/libstd/io/pipe.rs b/src/libstd/io/pipe.rs
new file mode 100644
index 00000000000..edbc6fa4285
--- /dev/null
+++ b/src/libstd/io/pipe.rs
@@ -0,0 +1,89 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Synchronous, in-memory pipes.
+//!
+//! Currently these aren't particularly useful, there only exists bindings
+//! enough so that pipes can be created to child processes.
+
+use prelude::*;
+use super::{Reader, Writer};
+use io::{io_error, EndOfFile};
+use io::native::file;
+use rt::rtio::{RtioPipe, with_local_io};
+
+pub struct PipeStream {
+    priv obj: ~RtioPipe,
+}
+
+impl PipeStream {
+    /// Consumes a file descriptor to return a pipe stream that will have
+    /// synchronous, but non-blocking reads/writes. This is useful if the file
+    /// descriptor is acquired via means other than the standard methods.
+    ///
+    /// This operation consumes ownership of the file descriptor and it will be
+    /// closed once the object is deallocated.
+    ///
+    /// # Example
+    ///
+    ///     use std::libc;
+    ///     use std::io::pipe;
+    ///
+    ///     let mut pipe = PipeStream::open(libc::STDERR_FILENO);
+    ///     pipe.write(bytes!("Hello, stderr!"));
+    ///
+    /// # Failure
+    ///
+    /// If the pipe cannot be created, an error will be raised on the
+    /// `io_error` condition.
+    pub fn open(fd: file::fd_t) -> Option<PipeStream> {
+        do with_local_io |io| {
+            match io.pipe_open(fd) {
+                Ok(obj) => Some(PipeStream { obj: obj }),
+                Err(e) => {
+                    io_error::cond.raise(e);
+                    None
+                }
+            }
+        }
+    }
+
+    pub fn new(inner: ~RtioPipe) -> PipeStream {
+        PipeStream { obj: inner }
+    }
+}
+
+impl Reader for PipeStream {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        match self.obj.read(buf) {
+            Ok(read) => Some(read),
+            Err(ioerr) => {
+                // EOF is indicated by returning None
+                if ioerr.kind != EndOfFile {
+                    io_error::cond.raise(ioerr);
+                }
+                return None;
+            }
+        }
+    }
+
+    fn eof(&mut self) -> bool { false }
+}
+
+impl Writer for PipeStream {
+    fn write(&mut self, buf: &[u8]) {
+        match self.obj.write(buf) {
+            Ok(_) => (),
+            Err(ioerr) => {
+                io_error::cond.raise(ioerr);
+            }
+        }
+    }
+}
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
new file mode 100644
index 00000000000..3941003d1c0
--- /dev/null
+++ b/src/libstd/io/process.rs
@@ -0,0 +1,177 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Bindings for executing child processes
+
+use prelude::*;
+use cell::Cell;
+
+use libc;
+use io;
+use io::io_error;
+use rt::rtio::{RtioProcess, IoFactory, with_local_io};
+
+use fmt;
+
+// windows values don't matter as long as they're at least one of unix's
+// TERM/KILL/INT signals
+#[cfg(windows)] pub static PleaseExitSignal: int = 15;
+#[cfg(windows)] pub static MustDieSignal: int = 9;
+#[cfg(not(windows))] pub static PleaseExitSignal: int = libc::SIGTERM as int;
+#[cfg(not(windows))] pub static MustDieSignal: int = libc::SIGKILL as int;
+
+pub struct Process {
+    priv handle: ~RtioProcess,
+    io: ~[Option<io::PipeStream>],
+}
+
+/// This configuration describes how a new process should be spawned. This is
+/// translated to libuv's own configuration
+pub struct ProcessConfig<'self> {
+    /// Path to the program to run
+    program: &'self str,
+
+    /// Arguments to pass to the program (doesn't include the program itself)
+    args: &'self [~str],
+
+    /// Optional environment to specify for the program. If this is None, then
+    /// it will inherit the current process's environment.
+    env: Option<&'self [(~str, ~str)]>,
+
+    /// Optional working directory for the new process. If this is None, then
+    /// the current directory of the running process is inherited.
+    cwd: Option<&'self str>,
+
+    /// Any number of streams/file descriptors/pipes may be attached to this
+    /// process. This list enumerates the file descriptors and such for the
+    /// process to be spawned, and the file descriptors inherited will start at
+    /// 0 and go to the length of this array.
+    ///
+    /// Standard file descriptors are:
+    ///
+    ///     0 - stdin
+    ///     1 - stdout
+    ///     2 - stderr
+    io: &'self [StdioContainer]
+}
+
+/// Describes what to do with a standard io stream for a child process.
+pub enum StdioContainer {
+    /// This stream will be ignored. This is the equivalent of attaching the
+    /// stream to `/dev/null`
+    Ignored,
+
+    /// The specified file descriptor is inherited for the stream which it is
+    /// specified for.
+    InheritFd(libc::c_int),
+
+    /// Creates a pipe for the specified file descriptor which will be created
+    /// when the process is spawned.
+    ///
+    /// The first boolean argument is whether the pipe is readable, and the
+    /// second is whether it is writable. These properties are from the view of
+    /// the *child* process, not the parent process.
+    CreatePipe(bool /* readable */, bool /* writable */),
+}
+
+/// Describes the result of a process after it has terminated.
+#[deriving(Eq)]
+pub enum ProcessExit {
+    /// Normal termination with an exit status.
+    ExitStatus(int),
+
+    /// Termination by signal, with the signal number.
+    ExitSignal(int),
+}
+
+impl fmt::Default for ProcessExit {
+    /// Format a ProcessExit enum, to nicely present the information.
+    fn fmt(obj: &ProcessExit, f: &mut fmt::Formatter) {
+        match *obj {
+            ExitStatus(code) =>  write!(f.buf, "exit code: {}", code),
+            ExitSignal(code) =>  write!(f.buf, "signal: {}", code),
+        }
+    }
+}
+
+impl ProcessExit {
+    /// Was termination successful? Signal termination not considered a success,
+    /// and success is defined as a zero exit status.
+    pub fn success(&self) -> bool {
+        return self.matches_exit_status(0);
+    }
+
+    /// Checks whether this ProcessExit matches the given exit status.
+    /// Termination by signal will never match an exit code.
+    pub fn matches_exit_status(&self, wanted: int) -> bool {
+        *self == ExitStatus(wanted)
+    }
+}
+
+impl Process {
+    /// Creates a new pipe initialized, but not bound to any particular
+    /// source/destination
+    pub fn new(config: ProcessConfig) -> Option<Process> {
+        let config = Cell::new(config);
+        do with_local_io |io| {
+            match io.spawn(config.take()) {
+                Ok((p, io)) => Some(Process{
+                    handle: p,
+                    io: io.move_iter().map(|p|
+                        p.map(|p| io::PipeStream::new(p))
+                    ).collect()
+                }),
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }
+    }
+
+    /// Returns the process id of this child process
+    pub fn id(&self) -> libc::pid_t { self.handle.id() }
+
+    /// Sends the specified signal to the child process, returning whether the
+    /// signal could be delivered or not.
+    ///
+    /// Note that this is purely a wrapper around libuv's `uv_process_kill`
+    /// function.
+    ///
+    /// If the signal delivery fails, then the `io_error` condition is raised on
+    pub fn signal(&mut self, signal: int) {
+        match self.handle.kill(signal) {
+            Ok(()) => {}
+            Err(err) => {
+                io_error::cond.raise(err)
+            }
+        }
+    }
+
+    /// Wait for the child to exit completely, returning the status that it
+    /// exited with. This function will continue to have the same return value
+    /// after it has been called at least once.
+    pub fn wait(&mut self) -> ProcessExit { self.handle.wait() }
+}
+
+impl Drop for Process {
+    fn drop(&mut self) {
+        // Close all I/O before exiting to ensure that the child doesn't wait
+        // forever to print some text or something similar.
+        for _ in range(0, self.io.len()) {
+            self.io.pop();
+        }
+
+        self.wait();
+    }
+}
+
+// Tests for this module can be found in the rtio-processes run-pass test, along
+// with the justification for why it's not located here.
diff --git a/src/libstd/io/signal.rs b/src/libstd/io/signal.rs
new file mode 100644
index 00000000000..1310152d05f
--- /dev/null
+++ b/src/libstd/io/signal.rs
@@ -0,0 +1,224 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+Signal handling
+
+This modules provides bindings to receive signals safely, built on top of the
+local I/O factory. There are a number of defined signals which can be caught,
+but not all signals will work across all platforms (windows doesn't have
+definitions for a number of signals.
+
+*/
+
+use clone::Clone;
+use comm::{Port, SharedChan, stream};
+use container::{Map, MutableMap};
+use hashmap;
+use io::io_error;
+use option::{Some, None};
+use result::{Err, Ok};
+use rt::rtio::{IoFactory, RtioSignal, with_local_io};
+
+#[repr(int)]
+#[deriving(Eq, IterBytes)]
+pub enum Signum {
+    /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
+    Break = 21i,
+    /// Equivalent to SIGHUP, delivered when the user closes the terminal
+    /// window. On delivery of HangUp, the program is given approximately
+    /// 10 seconds to perfom any cleanup. After that, Windows will
+    /// unconditionally terminate it.
+    HangUp = 1i,
+    /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
+    Interrupt = 2i,
+    /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
+    Quit = 3i,
+    /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
+    StopTemporarily = 20i,
+    /// Equivalent to SIGUSR1.
+    User1 = 10i,
+    /// Equivalent to SIGUSR2.
+    User2 = 12i,
+    /// Equivalent to SIGWINCH, delivered when the console has been resized.
+    /// WindowSizeChange may not be delivered in a timely manner; size change
+    /// will only be detected when the cursor is being moved.
+    WindowSizeChange = 28i,
+}
+
+/// Listener provides a port to listen for registered signals.
+///
+/// Listener automatically unregisters its handles once it is out of scope.
+/// However, clients can still unregister signums manually.
+///
+/// # Example
+///
+/// ```rust
+/// use std::io::signal::{Listener, Interrupt};
+///
+/// let mut listener = Listener::new();
+/// listener.register(signal::Interrupt);
+///
+/// do spawn {
+///     loop {
+///         match listener.port.recv() {
+///             Interrupt => println("Got Interrupt'ed"),
+///             _ => (),
+///         }
+///     }
+/// }
+///
+/// ```
+pub struct Listener {
+    /// A map from signums to handles to keep the handles in memory
+    priv handles: hashmap::HashMap<Signum, ~RtioSignal>,
+    /// chan is where all the handles send signums, which are received by
+    /// the clients from port.
+    priv chan: SharedChan<Signum>,
+
+    /// Clients of Listener can `recv()` from this port. This is exposed to
+    /// allow selection over this port as well as manipulation of the port
+    /// directly.
+    port: Port<Signum>,
+}
+
+impl Listener {
+    /// Creates a new listener for signals. Once created, signals are bound via
+    /// the `register` method (otherwise nothing will ever be received)
+    pub fn new() -> Listener {
+        let (port, chan) = stream();
+        Listener {
+            chan: SharedChan::new(chan),
+            port: port,
+            handles: hashmap::HashMap::new(),
+        }
+    }
+
+    /// Listen for a signal, returning true when successfully registered for
+    /// signum. Signals can be received using `recv()`.
+    ///
+    /// Once a signal is registered, this listener will continue to receive
+    /// notifications of signals until it is unregistered. This occurs
+    /// regardless of the number of other listeners registered in other tasks
+    /// (or on this task).
+    ///
+    /// Signals are still received if there is no task actively waiting for
+    /// a signal, and a later call to `recv` will return the signal that was
+    /// received while no task was waiting on it.
+    ///
+    /// # Failure
+    ///
+    /// If this function fails to register a signal handler, then an error will
+    /// be raised on the `io_error` condition and the function will return
+    /// false.
+    pub fn register(&mut self, signum: Signum) -> bool {
+        if self.handles.contains_key(&signum) {
+            return true; // self is already listening to signum, so succeed
+        }
+        do with_local_io |io| {
+            match io.signal(signum, self.chan.clone()) {
+                Ok(w) => {
+                    self.handles.insert(signum, w);
+                    Some(())
+                },
+                Err(ioerr) => {
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+        }.is_some()
+    }
+
+    /// Unregisters a signal. If this listener currently had a handler
+    /// registered for the signal, then it will stop receiving any more
+    /// notification about the signal. If the signal has already been received,
+    /// it may still be returned by `recv`.
+    pub fn unregister(&mut self, signum: Signum) {
+        self.handles.pop(&signum);
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use libc;
+    use io::timer;
+    use super::{Listener, Interrupt};
+    use comm::{GenericPort, Peekable};
+
+    // kill is only available on Unixes
+    #[cfg(unix)]
+    fn sigint() {
+        unsafe {
+            libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
+        }
+    }
+
+    #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
+    fn test_io_signal_smoketest() {
+        let mut signal = Listener::new();
+        signal.register(Interrupt);
+        sigint();
+        timer::sleep(10);
+        match signal.port.recv() {
+            Interrupt => (),
+            s => fail!("Expected Interrupt, got {:?}", s),
+        }
+    }
+
+    #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
+    fn test_io_signal_two_signal_one_signum() {
+        let mut s1 = Listener::new();
+        let mut s2 = Listener::new();
+        s1.register(Interrupt);
+        s2.register(Interrupt);
+        sigint();
+        timer::sleep(10);
+        match s1.port.recv() {
+            Interrupt => (),
+            s => fail!("Expected Interrupt, got {:?}", s),
+        }
+        match s2.port.recv() {
+            Interrupt => (),
+            s => fail!("Expected Interrupt, got {:?}", s),
+        }
+    }
+
+    #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
+    fn test_io_signal_unregister() {
+        let mut s1 = Listener::new();
+        let mut s2 = Listener::new();
+        s1.register(Interrupt);
+        s2.register(Interrupt);
+        s2.unregister(Interrupt);
+        sigint();
+        timer::sleep(10);
+        if s2.port.peek() {
+            fail!("Unexpected {:?}", s2.port.recv());
+        }
+    }
+
+    #[cfg(windows)]
+    #[test]
+    fn test_io_signal_invalid_signum() {
+        use io;
+        use super::User1;
+        let mut s = Listener::new();
+        let mut called = false;
+        do io::io_error::cond.trap(|_| {
+            called = true;
+        }).inside {
+            if s.register(User1) {
+                fail!("Unexpected successful registry of signum {:?}", User1);
+            }
+        }
+        assert!(called);
+    }
+}
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
new file mode 100644
index 00000000000..302d7579873
--- /dev/null
+++ b/src/libstd/io/stdio.rs
@@ -0,0 +1,321 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+This modules provides bindings to the local event loop's TTY interface, using it
+to have synchronous, but non-blocking versions of stdio. These handles can be
+inspected for information about terminal dimensions or related information
+about the stream or terminal that it is attached to.
+
+# Example
+
+```rust
+use std::io;
+
+let mut out = io::stdout();
+out.write(bytes!("Hello, world!"));
+```
+
+*/
+
+use fmt;
+use libc;
+use option::{Option, Some, None};
+use result::{Ok, Err};
+use io::buffered::LineBufferedWriter;
+use rt::rtio::{IoFactory, RtioTTY, RtioFileStream, with_local_io,
+               CloseAsynchronously};
+use super::{Reader, Writer, io_error, IoError, OtherIoError,
+            standard_error, EndOfFile};
+
+// And so begins the tale of acquiring a uv handle to a stdio stream on all
+// platforms in all situations. Our story begins by splitting the world into two
+// categories, windows and unix. Then one day the creators of unix said let
+// there be redirection! And henceforth there was redirection away from the
+// console for standard I/O streams.
+//
+// After this day, the world split into four factions:
+//
+// 1. Unix with stdout on a terminal.
+// 2. Unix with stdout redirected.
+// 3. Windows with stdout on a terminal.
+// 4. Windows with stdout redirected.
+//
+// Many years passed, and then one day the nation of libuv decided to unify this
+// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
+// These three ideas propagated throughout the lands and the four great factions
+// decided to settle among them.
+//
+// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
+// doing so, they even enhanced themselves further then their Pipe/File
+// brethren, becoming the dominant powers.
+//
+// The group of 4, however, decided to work independently. They abandoned the
+// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
+// The members of the 4th faction decided to only align themselves with File.
+//
+// tl;dr; TTY works on everything but when windows stdout is redirected, in that
+//        case pipe also doesn't work, but magically file does!
+enum StdSource {
+    TTY(~RtioTTY),
+    File(~RtioFileStream),
+}
+
+fn src<T>(fd: libc::c_int, readable: bool, f: &fn(StdSource) -> T) -> T {
+    do with_local_io |io| {
+        let fd = unsafe { libc::dup(fd) };
+        match io.tty_open(fd, readable) {
+            Ok(tty) => Some(f(TTY(tty))),
+            Err(_) => {
+                // It's not really that desirable if these handles are closed
+                // synchronously, and because they're squirreled away in a task
+                // structure the destructors will be run when the task is
+                // attempted to get destroyed. This means that if we run a
+                // synchronous destructor we'll attempt to do some scheduling
+                // operations which will just result in sadness.
+                Some(f(File(io.fs_from_raw_fd(fd, CloseAsynchronously))))
+            }
+        }
+    }.unwrap()
+}
+
+/// Creates a new non-blocking handle to the stdin of the current process.
+///
+/// See `stdout()` for notes about this function.
+pub fn stdin() -> StdReader {
+    do src(libc::STDIN_FILENO, true) |src| { StdReader { inner: src } }
+}
+
+/// Creates a new non-blocking handle to the stdout of the current process.
+///
+/// Note that this is a fairly expensive operation in that at least one memory
+/// allocation is performed. Additionally, this must be called from a runtime
+/// task context because the stream returned will be a non-blocking object using
+/// the local scheduler to perform the I/O.
+pub fn stdout() -> StdWriter {
+    do src(libc::STDOUT_FILENO, false) |src| { StdWriter { inner: src } }
+}
+
+/// Creates a new non-blocking handle to the stderr of the current process.
+///
+/// See `stdout()` for notes about this function.
+pub fn stderr() -> StdWriter {
+    do src(libc::STDERR_FILENO, false) |src| { StdWriter { inner: src } }
+}
+
+// Helper to access the local task's stdout handle
+//
+// Note that this is not a safe function to expose because you can create an
+// aliased pointer very easily:
+//
+//  do with_task_stdout |io1| {
+//      do with_task_stdout |io2| {
+//          // io1 aliases io2
+//      }
+//  }
+fn with_task_stdout(f: &fn(&mut Writer)) {
+    use rt::local::Local;
+    use rt::task::Task;
+
+    unsafe {
+        // Logging may require scheduling operations, so we can't remove the
+        // task from TLS right now, hence the unsafe borrow. Sad.
+        let task: *mut Task = Local::unsafe_borrow();
+
+        match (*task).stdout_handle {
+            Some(ref mut handle) => f(*handle),
+            None => {
+                let handle = stdout();
+                let mut handle = ~LineBufferedWriter::new(handle) as ~Writer;
+                f(handle);
+                (*task).stdout_handle = Some(handle);
+            }
+        }
+    }
+}
+
+/// Flushes the local task's stdout handle.
+///
+/// By default, this stream is a line-buffering stream, so flushing may be
+/// necessary to ensure that all output is printed to the screen (if there are
+/// no newlines printed).
+///
+/// Note that logging macros do not use this stream. Using the logging macros
+/// will emit output to stderr, and while they are line buffered the log
+/// messages are always terminated in a newline (no need to flush).
+pub fn flush() {
+    do with_task_stdout |io| {
+        io.flush();
+    }
+}
+
+/// Prints a string to the stdout of the current process. No newline is emitted
+/// after the string is printed.
+pub fn print(s: &str) {
+    do with_task_stdout |io| {
+        io.write(s.as_bytes());
+    }
+}
+
+/// Prints a string as a line. to the stdout of the current process. A literal
+/// `\n` character is printed to the console after the string.
+pub fn println(s: &str) {
+    do with_task_stdout |io| {
+        io.write(s.as_bytes());
+        io.write(['\n' as u8]);
+    }
+}
+
+/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
+/// with the `format_args!` macro.
+pub fn print_args(fmt: &fmt::Arguments) {
+    do with_task_stdout |io| {
+        fmt::write(io, fmt);
+    }
+}
+
+/// Similar to `println`, but takes a `fmt::Arguments` structure to be
+/// compatible with the `format_args!` macro.
+pub fn println_args(fmt: &fmt::Arguments) {
+    do with_task_stdout |io| {
+        fmt::writeln(io, fmt);
+    }
+}
+
+/// Representation of a reader of a standard input stream
+pub struct StdReader {
+    priv inner: StdSource
+}
+
+impl Reader for StdReader {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        let ret = match self.inner {
+            TTY(ref mut tty) => tty.read(buf),
+            File(ref mut file) => file.read(buf).map(|i| i as uint),
+        };
+        match ret {
+            // When reading a piped stdin, libuv will return 0-length reads when
+            // stdin reaches EOF. For pretty much all other streams it will
+            // return an actual EOF error, but apparently for stdin it's a
+            // little different. Hence, here we convert a 0 length read to an
+            // end-of-file indicator so the caller knows to stop reading.
+            Ok(0) => {
+                io_error::cond.raise(standard_error(EndOfFile));
+                None
+            }
+            Ok(amt) => Some(amt as uint),
+            Err(e) => {
+                io_error::cond.raise(e);
+                None
+            }
+        }
+    }
+
+    fn eof(&mut self) -> bool { false }
+}
+
+/// Representation of a writer to a standard output stream
+pub struct StdWriter {
+    priv inner: StdSource
+}
+
+impl StdWriter {
+    /// Gets the size of this output window, if possible. This is typically used
+    /// when the writer is attached to something like a terminal, this is used
+    /// to fetch the dimensions of the terminal.
+    ///
+    /// If successful, returns Some((width, height)).
+    ///
+    /// # Failure
+    ///
+    /// This function will raise on the `io_error` condition if an error
+    /// happens.
+    pub fn winsize(&mut self) -> Option<(int, int)> {
+        match self.inner {
+            TTY(ref mut tty) => {
+                match tty.get_winsize() {
+                    Ok(p) => Some(p),
+                    Err(e) => {
+                        io_error::cond.raise(e);
+                        None
+                    }
+                }
+            }
+            File(*) => {
+                io_error::cond.raise(IoError {
+                    kind: OtherIoError,
+                    desc: "stream is not a tty",
+                    detail: None,
+                });
+                None
+            }
+        }
+    }
+
+    /// Controls whether this output stream is a "raw stream" or simply a normal
+    /// stream.
+    ///
+    /// # Failure
+    ///
+    /// This function will raise on the `io_error` condition if an error
+    /// happens.
+    pub fn set_raw(&mut self, raw: bool) {
+        match self.inner {
+            TTY(ref mut tty) => {
+                match tty.set_raw(raw) {
+                    Ok(()) => {},
+                    Err(e) => io_error::cond.raise(e),
+                }
+            }
+            File(*) => {
+                io_error::cond.raise(IoError {
+                    kind: OtherIoError,
+                    desc: "stream is not a tty",
+                    detail: None,
+                });
+            }
+        }
+    }
+
+    /// Returns whether this stream is attached to a TTY instance or not.
+    pub fn isatty(&self) -> bool {
+        match self.inner {
+            TTY(*) => true,
+            File(*) => false,
+        }
+    }
+}
+
+impl Writer for StdWriter {
+    fn write(&mut self, buf: &[u8]) {
+        let ret = match self.inner {
+            TTY(ref mut tty) => tty.write(buf),
+            File(ref mut file) => file.write(buf),
+        };
+        match ret {
+            Ok(()) => {}
+            Err(e) => io_error::cond.raise(e)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn smoke() {
+        // Just make sure we can acquire handles
+        stdin();
+        stdout();
+        stderr();
+    }
+}
diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs
new file mode 100644
index 00000000000..219f63026d2
--- /dev/null
+++ b/src/libstd/io/timer.rs
@@ -0,0 +1,186 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+Synchronous Timers
+
+This module exposes the functionality to create timers, block the current task,
+and create ports which will receive notifications after a period of time.
+
+# Example
+
+```rust
+
+use std::io::Timer;
+
+let mut timer = Timer::new().unwrap();
+timer.sleep(10); // block the task for awhile
+
+let timeout = timer.oneshot(10);
+// do some work
+timeout.recv(); // wait for the timeout to expire
+
+let periodic = timer.periodic(10);
+loop {
+    periodic.recv();
+    // this loop is only executed once every 10ms
+}
+
+```
+
+*/
+
+use comm::{Port, PortOne};
+use option::{Option, Some, None};
+use result::{Ok, Err};
+use io::io_error;
+use rt::rtio::{IoFactory, RtioTimer, with_local_io};
+
+pub struct Timer {
+    priv obj: ~RtioTimer
+}
+
+/// Sleep the current task for `msecs` milliseconds.
+pub fn sleep(msecs: u64) {
+    let mut timer = Timer::new().expect("timer::sleep: could not create a Timer");
+
+    timer.sleep(msecs)
+}
+
+impl Timer {
+    /// Creates a new timer which can be used to put the current task to sleep
+    /// for a number of milliseconds, or to possibly create channels which will
+    /// get notified after an amount of time has passed.
+    pub fn new() -> Option<Timer> {
+        do with_local_io |io| {
+            match io.timer_init() {
+                Ok(t) => Some(Timer { obj: t }),
+                Err(ioerr) => {
+                    debug!("Timer::init: failed to init: {:?}", ioerr);
+                    io_error::cond.raise(ioerr);
+                    None
+                }
+            }
+
+        }
+    }
+
+    /// Blocks the current task for `msecs` milliseconds.
+    ///
+    /// Note that this function will cause any other ports for this timer to be
+    /// invalidated (the other end will be closed).
+    pub fn sleep(&mut self, msecs: u64) {
+        self.obj.sleep(msecs);
+    }
+
+    /// Creates a oneshot port which will have a notification sent when `msecs`
+    /// milliseconds has elapsed. This does *not* block the current task, but
+    /// instead returns immediately.
+    ///
+    /// Note that this invalidates any previous port which has been created by
+    /// this timer, and that the returned port will be invalidated once the
+    /// timer is destroyed (when it falls out of scope).
+    pub fn oneshot(&mut self, msecs: u64) -> PortOne<()> {
+        self.obj.oneshot(msecs)
+    }
+
+    /// Creates a port which will have a continuous stream of notifications
+    /// being sent every `msecs` milliseconds. This does *not* block the
+    /// current task, but instead returns immediately. The first notification
+    /// will not be received immediately, but rather after `msec` milliseconds
+    /// have passed.
+    ///
+    /// Note that this invalidates any previous port which has been created by
+    /// this timer, and that the returned port will be invalidated once the
+    /// timer is destroyed (when it falls out of scope).
+    pub fn periodic(&mut self, msecs: u64) -> Port<()> {
+        self.obj.period(msecs)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::*;
+    use super::*;
+    use rt::test::*;
+
+    #[test]
+    fn test_io_timer_sleep_simple() {
+        do run_in_mt_newsched_task {
+            let mut timer = Timer::new().unwrap();
+            timer.sleep(1);
+        }
+    }
+
+    #[test]
+    fn test_io_timer_sleep_oneshot() {
+        do run_in_mt_newsched_task {
+            let mut timer = Timer::new().unwrap();
+            timer.oneshot(1).recv();
+        }
+    }
+
+    #[test]
+    fn test_io_timer_sleep_oneshot_forget() {
+        do run_in_mt_newsched_task {
+            let mut timer = Timer::new().unwrap();
+            timer.oneshot(100000000000);
+        }
+    }
+
+    #[test]
+    fn oneshot_twice() {
+        do run_in_mt_newsched_task {
+            let mut timer = Timer::new().unwrap();
+            let port1 = timer.oneshot(10000);
+            let port = timer.oneshot(1);
+            port.recv();
+            assert_eq!(port1.try_recv(), None);
+        }
+    }
+
+    #[test]
+    fn test_io_timer_oneshot_then_sleep() {
+        do run_in_mt_newsched_task {
+            let mut timer = Timer::new().unwrap();
+            let port = timer.oneshot(100000000000);
+            timer.sleep(1); // this should invalidate the port
+
+            assert_eq!(port.try_recv(), None);
+        }
+    }
+
+    #[test]
+    fn test_io_timer_sleep_periodic() {
+        do run_in_mt_newsched_task {
+            let mut timer = Timer::new().unwrap();
+            let port = timer.periodic(1);
+            port.recv();
+            port.recv();
+            port.recv();
+        }
+    }
+
+    #[test]
+    fn test_io_timer_sleep_periodic_forget() {
+        do run_in_mt_newsched_task {
+            let mut timer = Timer::new().unwrap();
+            timer.periodic(100000000000);
+        }
+    }
+
+    #[test]
+    fn test_io_timer_sleep_standalone() {
+        do run_in_mt_newsched_task {
+            sleep(1)
+        }
+    }
+}