about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Turon <aturon@mozilla.com>2014-09-30 17:34:14 -0700
committerAaron Turon <aturon@mozilla.com>2014-11-08 20:40:38 -0800
commit0c1e1ff1e300868a29405a334e65eae690df971d (patch)
treed863379e4827ece4d4f6905bc091d60be86d7c1a
parent16470cf01b688c576f47b93bdb4af88db33cf1e1 (diff)
downloadrust-0c1e1ff1e300868a29405a334e65eae690df971d.tar.gz
rust-0c1e1ff1e300868a29405a334e65eae690df971d.zip
Runtime removal: refactor fs
This moves the filesystem implementation from libnative into the new
`sys` modules, refactoring along the way and hooking into `std::io::fs`.

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
-rw-r--r--src/libnative/io/file_unix.rs554
-rw-r--r--src/libnative/io/mod.rs83
-rw-r--r--src/libnative/io/timer_unix.rs2
-rw-r--r--src/librustrt/rtio.rs86
-rw-r--r--src/libstd/io/fs.rs304
-rw-r--r--src/libstd/io/mod.rs1
-rw-r--r--src/libstd/io/stdio.rs14
-rw-r--r--src/libstd/platform_imp/unix/fs.rs411
-rw-r--r--src/libstd/platform_imp/windows/fs.rs (renamed from src/libnative/io/file_windows.rs)373
9 files changed, 662 insertions, 1166 deletions
diff --git a/src/libnative/io/file_unix.rs b/src/libnative/io/file_unix.rs
deleted file mode 100644
index f616295c73d..00000000000
--- a/src/libnative/io/file_unix.rs
+++ /dev/null
@@ -1,554 +0,0 @@
-// Copyright 2013-2014 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
-
-use alloc::arc::Arc;
-use libc::{mod, c_int, c_void};
-use std::c_str::CString;
-use std::mem;
-use std::rt::rtio::{mod, IoResult};
-
-use io::{retry, keep_going};
-use io::util;
-
-pub type fd_t = libc::c_int;
-
-struct Inner {
-    fd: fd_t,
-    close_on_drop: bool,
-}
-
-pub struct FileDesc {
-    inner: Arc<Inner>
-}
-
-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 { inner: Arc::new(Inner {
-            fd: fd,
-            close_on_drop: close_on_drop
-        }) }
-    }
-
-    // FIXME(#10465) these functions should not be public, but anything in
-    //               native::io wanting to use them is forced to have all the
-    //               rtio traits in scope
-    pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        let ret = retry(|| unsafe {
-            libc::read(self.fd(),
-                       buf.as_mut_ptr() as *mut libc::c_void,
-                       buf.len() as libc::size_t)
-        });
-        if ret == 0 {
-            Err(util::eof())
-        } else if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(ret as uint)
-        }
-    }
-    pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
-        let ret = keep_going(buf, |buf, len| {
-            unsafe {
-                libc::write(self.fd(), buf as *const libc::c_void,
-                            len as libc::size_t) as i64
-            }
-        });
-        if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(())
-        }
-    }
-
-    pub fn fd(&self) -> fd_t { self.inner.fd }
-}
-
-impl rtio::RtioFileStream for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
-        self.inner_read(buf).map(|i| i as int)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
-        match retry(|| unsafe {
-            libc::pread(self.fd(), buf.as_ptr() as *mut _,
-                        buf.len() as libc::size_t,
-                        offset as libc::off_t)
-        }) {
-            -1 => Err(super::last_error()),
-            n => Ok(n as int)
-        }
-    }
-    fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe {
-            libc::pwrite(self.fd(), buf.as_ptr() as *const _,
-                         buf.len() as libc::size_t, offset as libc::off_t)
-        }))
-    }
-    fn seek(&mut self, pos: i64, whence: rtio::SeekStyle) -> IoResult<u64> {
-        let whence = match whence {
-            rtio::SeekSet => libc::SEEK_SET,
-            rtio::SeekEnd => libc::SEEK_END,
-            rtio::SeekCur => libc::SEEK_CUR,
-        };
-        let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
-        if n < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(n as u64)
-        }
-    }
-    fn tell(&self) -> IoResult<u64> {
-        let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
-        if n < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(n as u64)
-        }
-    }
-    fn fsync(&mut self) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
-    }
-    fn datasync(&mut self) -> IoResult<()> {
-        return super::mkerr_libc(os_datasync(self.fd()));
-
-        #[cfg(any(target_os = "macos", target_os = "ios"))]
-        fn os_datasync(fd: c_int) -> c_int {
-            unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
-        }
-        #[cfg(target_os = "linux")]
-        fn os_datasync(fd: c_int) -> c_int {
-            retry(|| unsafe { libc::fdatasync(fd) })
-        }
-        #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))]
-        fn os_datasync(fd: c_int) -> c_int {
-            retry(|| unsafe { libc::fsync(fd) })
-        }
-    }
-    fn truncate(&mut self, offset: i64) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe {
-            libc::ftruncate(self.fd(), offset as libc::off_t)
-        }))
-    }
-
-    fn fstat(&mut self) -> IoResult<rtio::FileStat> {
-        let mut stat: libc::stat = unsafe { mem::zeroed() };
-        match unsafe { libc::fstat(self.fd(), &mut stat) } {
-            0 => Ok(mkstat(&stat)),
-            _ => Err(super::last_error()),
-        }
-    }
-}
-
-impl rtio::RtioPipe for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn clone(&self) -> Box<rtio::RtioPipe + Send> {
-        box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
-    }
-
-    // Only supported on named pipes currently. Note that this doesn't have an
-    // impact on the std::io primitives, this is never called via
-    // std::io::PipeStream. If the functionality is exposed in the future, then
-    // these methods will need to be implemented.
-    fn close_read(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn close_write(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn set_timeout(&mut self, _t: Option<u64>) {}
-    fn set_read_timeout(&mut self, _t: Option<u64>) {}
-    fn set_write_timeout(&mut self, _t: Option<u64>) {}
-}
-
-impl rtio::RtioTTY for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn get_winsize(&mut self) -> IoResult<(int, int)> {
-        Err(super::unimpl())
-    }
-    fn isatty(&self) -> bool { false }
-}
-
-impl Drop for Inner {
-    fn drop(&mut self) {
-        // closing stdio file handles makes no sense, so never do it. Also, note
-        // that errors are ignored when closing a file descriptor. The reason
-        // for this is that if an error occurs we don't actually know if the
-        // file descriptor was closed or not, and if we retried (for something
-        // like EINTR), we might close another valid file descriptor (opened
-        // after we closed ours.
-        if self.close_on_drop && self.fd > libc::STDERR_FILENO {
-            let n = unsafe { libc::close(self.fd) };
-            if n != 0 {
-                println!("error {} when closing file descriptor {}", n,
-                         self.fd);
-            }
-        }
-    }
-}
-
-pub struct CFile {
-    file: *mut libc::FILE,
-    fd: FileDesc,
-}
-
-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: *mut libc::FILE) -> CFile {
-        CFile {
-            file: file,
-            fd: FileDesc::new(unsafe { libc::fileno(file) }, false)
-        }
-    }
-
-    pub fn flush(&mut self) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) }))
-    }
-}
-
-impl rtio::RtioFileStream for CFile {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
-        let ret = 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 {
-            Err(util::eof())
-        } else if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(ret as int)
-        }
-    }
-
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        let ret = keep_going(buf, |buf, len| {
-            unsafe {
-                libc::fwrite(buf as *const libc::c_void, 1, len as libc::size_t,
-                            self.file) as i64
-            }
-        });
-        if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(())
-        }
-    }
-
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
-        self.flush().and_then(|()| self.fd.pread(buf, offset))
-    }
-    fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.pwrite(buf, offset))
-    }
-    fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
-        let whence = match style {
-            rtio::SeekSet => libc::SEEK_SET,
-            rtio::SeekEnd => libc::SEEK_END,
-            rtio::SeekCur => libc::SEEK_CUR,
-        };
-        let n = unsafe { libc::fseek(self.file, pos as libc::c_long, whence) };
-        if n < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(n as u64)
-        }
-    }
-    fn tell(&self) -> IoResult<u64> {
-        let ret = unsafe { libc::ftell(self.file) };
-        if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(ret as u64)
-        }
-    }
-    fn fsync(&mut self) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.fsync())
-    }
-    fn datasync(&mut self) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.datasync())
-    }
-    fn truncate(&mut self, offset: i64) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.truncate(offset))
-    }
-
-    fn fstat(&mut self) -> IoResult<rtio::FileStat> {
-        self.flush().and_then(|()| self.fd.fstat())
-    }
-}
-
-impl Drop for CFile {
-    fn drop(&mut self) {
-        unsafe { let _ = libc::fclose(self.file); }
-    }
-}
-
-pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
-    -> IoResult<FileDesc>
-{
-    let flags = match fm {
-        rtio::Open => 0,
-        rtio::Append => libc::O_APPEND,
-        rtio::Truncate => libc::O_TRUNC,
-    };
-    // Opening with a write permission must silently create the file.
-    let (flags, mode) = match fa {
-        rtio::Read => (flags | libc::O_RDONLY, 0),
-        rtio::Write => (flags | libc::O_WRONLY | libc::O_CREAT,
-                        libc::S_IRUSR | libc::S_IWUSR),
-        rtio::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
-                            libc::S_IRUSR | libc::S_IWUSR),
-    };
-
-    match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
-        -1 => Err(super::last_error()),
-        fd => Ok(FileDesc::new(fd, true)),
-    }
-}
-
-pub fn mkdir(p: &CString, mode: uint) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
-}
-
-pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
-    use libc::{dirent_t};
-    use libc::{opendir, readdir_r, closedir};
-
-    fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
-        let root = unsafe { CString::new(root.as_ptr(), false) };
-        let root = Path::new(root);
-
-        dirs.into_iter().filter(|path| {
-            path.as_vec() != b"." && path.as_vec() != b".."
-        }).map(|path| root.join(path).to_c_str()).collect()
-    }
-
-    extern {
-        fn rust_dirent_t_size() -> libc::c_int;
-        fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char;
-    }
-
-    let size = unsafe { rust_dirent_t_size() };
-    let mut buf = Vec::<u8>::with_capacity(size as uint);
-    let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t;
-
-    let dir_ptr = unsafe {opendir(p.as_ptr())};
-
-    if dir_ptr as uint != 0 {
-        let mut paths = vec!();
-        let mut entry_ptr = 0 as *mut dirent_t;
-        while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
-            if entry_ptr.is_null() { break }
-            let cstr = unsafe {
-                CString::new(rust_list_dir_val(entry_ptr), false)
-            };
-            paths.push(Path::new(cstr));
-        }
-        assert_eq!(unsafe { closedir(dir_ptr) }, 0);
-        Ok(prune(p, paths))
-    } else {
-        Err(super::last_error())
-    }
-}
-
-pub fn unlink(p: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
-}
-
-pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })
-}
-
-pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
-    super::mkerr_libc(retry(|| unsafe {
-        libc::chmod(p.as_ptr(), mode as libc::mode_t)
-    }))
-}
-
-pub fn rmdir(p: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
-}
-
-pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> {
-    super::mkerr_libc(retry(|| unsafe {
-        libc::chown(p.as_ptr(), uid as libc::uid_t,
-                    gid as libc::gid_t)
-    }))
-}
-
-pub fn readlink(p: &CString) -> IoResult<CString> {
-    let p = p.as_ptr();
-    let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
-    if len == -1 {
-        len = 1024; // FIXME: read PATH_MAX from C ffi?
-    }
-    let mut buf: Vec<u8> = Vec::with_capacity(len as uint);
-    match unsafe {
-        libc::readlink(p, buf.as_ptr() as *mut libc::c_char,
-                       len as libc::size_t) as libc::c_int
-    } {
-        -1 => Err(super::last_error()),
-        n => {
-            assert!(n > 0);
-            unsafe { buf.set_len(n as uint); }
-            Ok(buf.as_slice().to_c_str())
-        }
-    }
-}
-
-pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
-}
-
-pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
-}
-
-fn mkstat(stat: &libc::stat) -> rtio::FileStat {
-    // FileStat times are in milliseconds
-    fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
-
-    #[cfg(not(any(target_os = "linux", target_os = "android")))]
-    fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    fn flags(_stat: &libc::stat) -> u64 { 0 }
-
-    #[cfg(not(any(target_os = "linux", target_os = "android")))]
-    fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    fn gen(_stat: &libc::stat) -> u64 { 0 }
-
-    rtio::FileStat {
-        size: stat.st_size as u64,
-        kind: stat.st_mode as u64,
-        perm: stat.st_mode as u64,
-        created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
-        modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
-        accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
-        device: stat.st_dev as u64,
-        inode: stat.st_ino as u64,
-        rdev: stat.st_rdev as u64,
-        nlink: stat.st_nlink as u64,
-        uid: stat.st_uid as u64,
-        gid: stat.st_gid as u64,
-        blksize: stat.st_blksize as u64,
-        blocks: stat.st_blocks as u64,
-        flags: flags(stat),
-        gen: gen(stat),
-    }
-}
-
-pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
-    let mut stat: libc::stat = unsafe { mem::zeroed() };
-    match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
-        0 => Ok(mkstat(&stat)),
-        _ => Err(super::last_error()),
-    }
-}
-
-pub fn lstat(p: &CString) -> IoResult<rtio::FileStat> {
-    let mut stat: libc::stat = unsafe { mem::zeroed() };
-    match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
-        0 => Ok(mkstat(&stat)),
-        _ => Err(super::last_error()),
-    }
-}
-
-pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
-    let buf = libc::utimbuf {
-        actime: (atime / 1000) as libc::time_t,
-        modtime: (mtime / 1000) as libc::time_t,
-    };
-    super::mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) })
-}
-
-#[cfg(test)]
-mod tests {
-    use super::{CFile, FileDesc};
-    use libc;
-    use std::os;
-    use std::rt::rtio::{RtioFileStream, SeekSet};
-
-    #[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer
-    #[test]
-    fn test_file_desc() {
-        // Run this test with some pipes so we don't have to mess around with
-        // opening or closing files.
-        let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
-        let mut reader = FileDesc::new(reader, true);
-        let mut writer = FileDesc::new(writer, true);
-
-        writer.inner_write(b"test").ok().unwrap();
-        let mut buf = [0u8, ..4];
-        match reader.inner_read(buf) {
-            Ok(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 => panic!("invalid read: {}", r),
-        }
-
-        assert!(writer.inner_read(buf).is_err());
-        assert!(reader.inner_write(buf).is_err());
-    }
-
-    #[test]
-    fn test_cfile() {
-        unsafe {
-            let f = libc::tmpfile();
-            assert!(!f.is_null());
-            let mut file = CFile::new(f);
-
-            file.write(b"test").ok().unwrap();
-            let mut buf = [0u8, ..4];
-            let _ = file.seek(0, SeekSet).ok().unwrap();
-            match file.read(buf) {
-                Ok(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 => panic!("invalid read: {}", r)
-            }
-        }
-    }
-}
diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index a541712e17f..baf58b83dcd 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -30,7 +30,6 @@ use std::rt::rtio::{mod, IoResult, IoError};
 use std::num;
 
 // Local re-exports
-pub use self::file::FileDesc;
 pub use self::process::Process;
 
 mod helper_thread;
@@ -41,13 +40,6 @@ pub mod net;
 pub mod process;
 mod util;
 
-#[cfg(unix)]
-#[path = "file_unix.rs"]
-pub mod file;
-#[cfg(windows)]
-#[path = "file_windows.rs"]
-pub mod file;
-
 #[cfg(any(target_os = "macos",
           target_os = "ios",
           target_os = "freebsd",
@@ -92,25 +84,6 @@ fn last_error() -> IoError {
     }
 }
 
-// unix has nonzero values as errors
-fn mkerr_libc <Int: num::Zero>(ret: Int) -> IoResult<()> {
-    if !ret.is_zero() {
-        Err(last_error())
-    } else {
-        Ok(())
-    }
-}
-
-// windows has zero values as errors
-#[cfg(windows)]
-fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
-    if ret == 0 {
-        Err(last_error())
-    } else {
-        Ok(())
-    }
-}
-
 #[cfg(windows)]
 #[inline]
 fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
@@ -199,62 +172,6 @@ impl rtio::IoFactory for IoFactory {
         addrinfo::GetAddrInfoRequest::run(host, servname, hint)
     }
 
-    // filesystem operations
-    fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior)
-                      -> Box<rtio::RtioFileStream + Send> {
-        let close = match close {
-            rtio::CloseSynchronously | rtio::CloseAsynchronously => true,
-            rtio::DontClose => false
-        };
-        box file::FileDesc::new(fd, close) as Box<rtio::RtioFileStream + Send>
-    }
-    fn fs_open(&mut self, path: &CString, fm: rtio::FileMode,
-               fa: rtio::FileAccess)
-        -> IoResult<Box<rtio::RtioFileStream + Send>>
-    {
-        file::open(path, fm, fa).map(|fd| box fd as Box<rtio::RtioFileStream + Send>)
-    }
-    fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
-        file::unlink(path)
-    }
-    fn fs_stat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
-        file::stat(path)
-    }
-    fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()> {
-        file::mkdir(path, mode)
-    }
-    fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()> {
-        file::chmod(path, mode)
-    }
-    fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
-        file::rmdir(path)
-    }
-    fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> {
-        file::rename(path, to)
-    }
-    fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<Vec<CString>> {
-        file::readdir(path)
-    }
-    fn fs_lstat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
-        file::lstat(path)
-    }
-    fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> {
-        file::chown(path, uid, gid)
-    }
-    fn fs_readlink(&mut self, path: &CString) -> IoResult<CString> {
-        file::readlink(path)
-    }
-    fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
-        file::symlink(src, dst)
-    }
-    fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
-        file::link(src, dst)
-    }
-    fn fs_utime(&mut self, src: &CString, atime: u64,
-                mtime: u64) -> IoResult<()> {
-        file::utime(src, atime, mtime)
-    }
-
     // misc
     fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
         timer::Timer::new().map(|t| box t as Box<rtio::RtioTimer + Send>)
diff --git a/src/libnative/io/timer_unix.rs b/src/libnative/io/timer_unix.rs
index 38895f2a8f9..c26e2e76cee 100644
--- a/src/libnative/io/timer_unix.rs
+++ b/src/libnative/io/timer_unix.rs
@@ -56,7 +56,7 @@ use std::sync::atomic;
 use std::comm;
 
 use io::c;
-use io::file::FileDesc;
+use platform_imp::fs::FileDesc;
 use io::helper_thread::Helper;
 
 helper_init!(static HELPER: Helper<Req>)
diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs
index e9bee19b498..1f3ef60e6fb 100644
--- a/src/librustrt/rtio.rs
+++ b/src/librustrt/rtio.rs
@@ -50,20 +50,6 @@ pub trait RemoteCallback {
     fn fire(&mut self);
 }
 
-/// Description of what to do when a file handle is closed
-pub enum CloseBehavior {
-    /// Do not close this handle when the object is destroyed
-    DontClose,
-    /// Synchronously close the handle, meaning that the task will block when
-    /// the handle is destroyed until it has been fully closed.
-    CloseSynchronously,
-    /// Asynchronously closes a handle, meaning that the task will *not* block
-    /// when the handle is destroyed, but the handle will still get deallocated
-    /// and cleaned up (but this will happen asynchronously on the local event
-    /// loop).
-    CloseAsynchronously,
-}
-
 /// Data needed to spawn a process. Serializes the `std::io::process::Command`
 /// builder.
 pub struct ProcessConfig<'a> {
@@ -202,28 +188,6 @@ pub trait IoFactory {
                           hint: Option<AddrinfoHint>)
                           -> IoResult<Vec<AddrinfoInfo>>;
 
-    // filesystem operations
-    fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior)
-                      -> Box<RtioFileStream + Send>;
-    fn fs_open(&mut self, path: &CString, fm: FileMode, fa: FileAccess)
-               -> IoResult<Box<RtioFileStream + Send>>;
-    fn fs_unlink(&mut self, path: &CString) -> IoResult<()>;
-    fn fs_stat(&mut self, path: &CString) -> IoResult<FileStat>;
-    fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()>;
-    fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()>;
-    fn fs_rmdir(&mut self, path: &CString) -> IoResult<()>;
-    fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()>;
-    fn fs_readdir(&mut self, path: &CString, flags: c_int) ->
-        IoResult<Vec<CString>>;
-    fn fs_lstat(&mut self, path: &CString) -> IoResult<FileStat>;
-    fn fs_chown(&mut self, path: &CString, uid: int, gid: int) ->
-        IoResult<()>;
-    fn fs_readlink(&mut self, path: &CString) -> IoResult<CString>;
-    fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
-    fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
-    fn fs_utime(&mut self, src: &CString, atime: u64, mtime: u64) ->
-        IoResult<()>;
-
     // misc
     fn timer_init(&mut self) -> IoResult<Box<RtioTimer + Send>>;
     fn spawn(&mut self, cfg: ProcessConfig)
@@ -296,19 +260,6 @@ pub trait RtioTimer {
     fn period(&mut self, msecs: u64, cb: Box<Callback + Send>);
 }
 
-pub trait RtioFileStream {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int>;
-    fn write(&mut self, buf: &[u8]) -> IoResult<()>;
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int>;
-    fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()>;
-    fn seek(&mut self, pos: i64, whence: SeekStyle) -> IoResult<u64>;
-    fn tell(&self) -> IoResult<u64>;
-    fn fsync(&mut self) -> IoResult<()>;
-    fn datasync(&mut self) -> IoResult<()>;
-    fn truncate(&mut self, offset: i64) -> IoResult<()>;
-    fn fstat(&mut self) -> IoResult<FileStat>;
-}
-
 pub trait RtioProcess {
     fn id(&self) -> libc::pid_t;
     fn kill(&mut self, signal: int) -> IoResult<()>;
@@ -399,43 +350,6 @@ pub enum ProcessExit {
     ExitSignal(int),
 }
 
-pub enum FileMode {
-    Open,
-    Append,
-    Truncate,
-}
-
-pub enum FileAccess {
-    Read,
-    Write,
-    ReadWrite,
-}
-
-pub struct FileStat {
-    pub size: u64,
-    pub kind: u64,
-    pub perm: u64,
-    pub created: u64,
-    pub modified: u64,
-    pub accessed: u64,
-    pub device: u64,
-    pub inode: u64,
-    pub rdev: u64,
-    pub nlink: u64,
-    pub uid: u64,
-    pub gid: u64,
-    pub blksize: u64,
-    pub blocks: u64,
-    pub flags: u64,
-    pub gen: u64,
-}
-
-pub enum SeekStyle {
-    SeekSet,
-    SeekEnd,
-    SeekCur,
-}
-
 pub struct AddrinfoHint {
     pub family: uint,
     pub socktype: uint,
diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs
index e76046bac05..5c2a5c3512d 100644
--- a/src/libstd/io/fs.rs
+++ b/src/libstd/io/fs.rs
@@ -52,28 +52,25 @@ fs::unlink(&path);
 
 */
 
-use c_str::ToCStr;
 use clone::Clone;
 use io::standard_error;
-use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
+use io::{FilePermission, Write, Open, FileAccess, FileMode};
 use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
-use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
+use io::{Read, Truncate, ReadWrite, Append};
 use io::UpdateIoError;
 use io;
 use iter::{Iterator, Extend};
-use kinds::Send;
-use libc;
 use option::{Some, None, Option};
-use boxed::Box;
 use path::{Path, GenericPath};
 use path;
 use result::{Err, Ok};
-use rt::rtio::LocalIo;
-use rt::rtio;
 use slice::SlicePrelude;
 use string::String;
 use vec::Vec;
 
+use sys::fs as fs_imp;
+use sys_common;
+
 /// Unconstrained file access type that exposes read and write operations
 ///
 /// Can be constructed via `File::open()`, `File::create()`, and
@@ -86,11 +83,17 @@ use vec::Vec;
 /// configured at creation time, via the `FileAccess` parameter to
 /// `File::open_mode()`.
 pub struct File {
-    fd: Box<rtio::RtioFileStream + Send>,
+    fd: fs_imp::FileDesc,
     path: Path,
     last_nread: int,
 }
 
+impl sys_common::AsFileDesc for File {
+    fn as_fd(&self) -> &fs_imp::FileDesc {
+        &self.fd
+    }
+}
+
 impl File {
     /// Open a file at `path` in the mode specified by the `mode` and `access`
     /// arguments
@@ -133,26 +136,13 @@ impl File {
     pub fn open_mode(path: &Path,
                      mode: FileMode,
                      access: FileAccess) -> IoResult<File> {
-        let rtio_mode = match mode {
-            Open => rtio::Open,
-            Append => rtio::Append,
-            Truncate => rtio::Truncate,
-        };
-        let rtio_access = match access {
-            Read => rtio::Read,
-            Write => rtio::Write,
-            ReadWrite => rtio::ReadWrite,
-        };
-        let err = LocalIo::maybe_raise(|io| {
-            io.fs_open(&path.to_c_str(), rtio_mode, rtio_access).map(|fd| {
-                File {
-                    path: path.clone(),
-                    fd: fd,
-                    last_nread: -1
-                }
-            })
-        }).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't open file", |e| {
+        fs_imp::open(path, mode, access).map(|fd| {
+            File {
+                path: path.clone(),
+                fd: fd,
+                last_nread: -1
+            }
+        }).update_err("couldn't open file", |e| {
             format!("{}; path={}; mode={}; access={}", e, path.display(),
                 mode_string(mode), access_string(access))
         })
@@ -194,7 +184,7 @@ impl File {
     /// ```
     pub fn create(path: &Path) -> IoResult<File> {
         File::open_mode(path, Truncate, Write)
-            .update_desc("couldn't create file")
+             .update_desc("couldn't create file")
     }
 
     /// Returns the original path which was used to open this file.
@@ -206,9 +196,9 @@ impl File {
     /// device. This will flush any internal buffers necessary to perform this
     /// operation.
     pub fn fsync(&mut self) -> IoResult<()> {
-        let err = self.fd.fsync().map_err(IoError::from_rtio_error);
-        err.update_err("couldn't fsync file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.fsync()
+            .update_err("couldn't fsync file",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 
     /// This function is similar to `fsync`, except that it may not synchronize
@@ -216,9 +206,9 @@ impl File {
     /// must synchronize content, but don't need the metadata on disk. The goal
     /// of this method is to reduce disk operations.
     pub fn datasync(&mut self) -> IoResult<()> {
-        let err = self.fd.datasync().map_err(IoError::from_rtio_error);
-        err.update_err("couldn't datasync file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.datasync()
+            .update_err("couldn't datasync file",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 
     /// Either truncates or extends the underlying file, updating the size of
@@ -230,10 +220,9 @@ impl File {
     /// will be extended to `size` and have all of the intermediate data filled
     /// in with 0s.
     pub fn truncate(&mut self, size: i64) -> IoResult<()> {
-        let err = self.fd.truncate(size).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't truncate file", |e| {
-            format!("{}; path={}; size={}", e, self.path.display(), size)
-        })
+        self.fd.truncate(size)
+            .update_err("couldn't truncate file", |e|
+                format!("{}; path={}; size={}", e, self.path.display(), size))
     }
 
     /// Returns true if the stream has reached the end of the file.
@@ -251,12 +240,9 @@ impl File {
 
     /// Queries information about the underlying file.
     pub fn stat(&mut self) -> IoResult<FileStat> {
-        let err = match self.fd.fstat() {
-            Ok(s) => Ok(from_rtio(s)),
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        };
-        err.update_err("couldn't fstat file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.fstat()
+            .update_err("couldn't fstat file", |e|
+                format!("{}; path={}", e, self.path.display()))
     }
 }
 
@@ -282,41 +268,9 @@ impl File {
 /// user lacks permissions to remove the file, or if some other filesystem-level
 /// error occurs.
 pub fn unlink(path: &Path) -> IoResult<()> {
-    return match do_unlink(path) {
-        Ok(()) => Ok(()),
-        Err(e) => {
-            // On unix, a readonly file can be successfully removed. On windows,
-            // however, it cannot. To keep the two platforms in line with
-            // respect to their behavior, catch this case on windows, attempt to
-            // change it to read-write, and then remove the file.
-            if cfg!(windows) && e.kind == io::PermissionDenied {
-                let stat = match stat(path) {
-                    Ok(stat) => stat,
-                    Err(..) => return Err(e),
-                };
-                if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
-
-                match chmod(path, stat.perm | io::USER_WRITE) {
-                    Ok(()) => do_unlink(path),
-                    Err(..) => {
-                        // Try to put it back as we found it
-                        let _ = chmod(path, stat.perm);
-                        Err(e)
-                    }
-                }
-            } else {
-                Err(e)
-            }
-        }
-    };
-
-    fn do_unlink(path: &Path) -> IoResult<()> {
-        let err = LocalIo::maybe_raise(|io| {
-            io.fs_unlink(&path.to_c_str())
-        }).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't unlink path",
-                       |e| format!("{}; path={}", e, path.display()))
-    }
+    fs_imp::unlink(path)
+           .update_err("couldn't unlink path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Given a path, query the file system to get information about a file,
@@ -341,12 +295,9 @@ pub fn unlink(path: &Path) -> IoResult<()> {
 /// 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) -> IoResult<FileStat> {
-    let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) {
-        Ok(s) => Ok(from_rtio(s)),
-        Err(e) => Err(IoError::from_rtio_error(e)),
-    };
-    err.update_err("couldn't stat path",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::stat(path)
+           .update_err("couldn't stat path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Perform the same operation as the `stat` function, except that this
@@ -358,53 +309,9 @@ pub fn stat(path: &Path) -> IoResult<FileStat> {
 ///
 /// See `stat`
 pub fn lstat(path: &Path) -> IoResult<FileStat> {
-    let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) {
-        Ok(s) => Ok(from_rtio(s)),
-        Err(e) => Err(IoError::from_rtio_error(e)),
-    };
-    err.update_err("couldn't lstat path",
-                   |e| format!("{}; path={}", e, path.display()))
-}
-
-fn from_rtio(s: rtio::FileStat) -> FileStat {
-    #[cfg(windows)]
-    type Mode = libc::c_int;
-    #[cfg(unix)]
-    type Mode = libc::mode_t;
-
-    let rtio::FileStat {
-        size, kind, perm, created, modified,
-        accessed, device, inode, rdev,
-        nlink, uid, gid, blksize, blocks, flags, gen
-    } = s;
-
-    FileStat {
-        size: size,
-        kind: match (kind as Mode) & libc::S_IFMT {
-            libc::S_IFREG => io::TypeFile,
-            libc::S_IFDIR => io::TypeDirectory,
-            libc::S_IFIFO => io::TypeNamedPipe,
-            libc::S_IFBLK => io::TypeBlockSpecial,
-            libc::S_IFLNK => io::TypeSymlink,
-            _ => io::TypeUnknown,
-        },
-        perm: FilePermission::from_bits_truncate(perm as u32),
-        created: created,
-        modified: modified,
-        accessed: accessed,
-        unstable: UnstableFileStat {
-            device: device,
-            inode: inode,
-            rdev: rdev,
-            nlink: nlink,
-            uid: uid,
-            gid: gid,
-            blksize: blksize,
-            blocks: blocks,
-            flags: flags,
-            gen: gen,
-        },
-    }
+    fs_imp::lstat(path)
+           .update_err("couldn't lstat path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Rename a file or directory to a new name.
@@ -424,12 +331,9 @@ fn from_rtio(s: rtio::FileStat) -> FileStat {
 /// the process lacks permissions to view the contents, or if some other
 /// intermittent I/O error occurs.
 pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_rename(&from.to_c_str(), &to.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't rename path", |e| {
-        format!("{}; from={}; to={}", e, from.display(), to.display())
-    })
+    fs_imp::rename(from, to)
+           .update_err("couldn't rename path", |e|
+               format!("{}; from={}; to={}", e, from.display(), to.display()))
 }
 
 /// Copies the contents of one file to another. This function will also
@@ -462,8 +366,9 @@ pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
 /// being created and then destroyed by this operation.
 pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
     fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> {
-        result.update_err("couldn't copy path",
-            |e| format!("{}; from={}; to={}", e, from.display(), to.display()))
+        result.update_err("couldn't copy path", |e| {
+            format!("{}; from={}; to={}", e, from.display(), to.display())
+        })
     }
 
     if !from.is_file() {
@@ -512,45 +417,33 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
 /// the process lacks permissions to change the attributes of the file, or if
 /// some other I/O error is encountered.
 pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_chmod(&path.to_c_str(), mode.bits() as uint)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't chmod path", |e| {
-        format!("{}; path={}; mode={}", e, path.display(), mode)
-    })
+    fs_imp::chmod(path, mode.bits() as uint)
+           .update_err("couldn't chmod path", |e|
+               format!("{}; path={}; mode={}", e, path.display(), mode))
 }
 
 /// Change the user and group owners of a file at the specified path.
 pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_chown(&path.to_c_str(), uid, gid)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't chown path", |e| {
-        format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)
-    })
+    fs_imp::chown(path, uid, gid)
+           .update_err("couldn't chown path", |e|
+               format!("{}; path={}; uid={}; gid={}", e, path.display(), 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.
 pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_link(&src.to_c_str(), &dst.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't link path", |e| {
-        format!("{}; src={}; dest={}", e, src.display(), dst.display())
-    })
+    fs_imp::link(src, dst)
+           .update_err("couldn't link path", |e|
+               format!("{}; src={}; dest={}", e, src.display(), dst.display()))
 }
 
 /// Creates a new symbolic link on the filesystem. The `dst` path will be a
 /// symlink pointing to the `src` path.
 pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_symlink(&src.to_c_str(), &dst.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't symlink path", |e| {
-        format!("{}; src={}; dest={}", e, src.display(), dst.display())
-    })
+    fs_imp::symlink(src, dst)
+           .update_err("couldn't symlink path", |e|
+               format!("{}; src={}; dest={}", e, src.display(), dst.display()))
 }
 
 /// Reads a symlink, returning the file that the symlink points to.
@@ -560,11 +453,9 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
 /// This function will return an error 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) -> IoResult<Path> {
-    let err = LocalIo::maybe_raise(|io| {
-        Ok(Path::new(try!(io.fs_readlink(&path.to_c_str()))))
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't resolve symlink for path",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::readlink(path)
+           .update_err("couldn't resolve symlink for path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Create a new, empty directory at the provided path
@@ -585,12 +476,9 @@ pub fn readlink(path: &Path) -> IoResult<Path> {
 /// This function will return an error 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) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_mkdir(&path.to_c_str(), mode.bits() as uint)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't create directory", |e| {
-        format!("{}; path={}; mode={}", e, path.display(), mode)
-    })
+    fs_imp::mkdir(path, mode.bits() as uint)
+           .update_err("couldn't create directory", |e|
+               format!("{}; path={}; mode={}", e, path.display(), mode))
 }
 
 /// Remove an existing, empty directory
@@ -610,11 +498,9 @@ pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
 /// This function will return an error 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) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_rmdir(&path.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't remove directory",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::rmdir(path)
+           .update_err("couldn't remove directory", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Retrieve a vector containing all entries within a provided directory
@@ -650,13 +536,9 @@ pub fn rmdir(path: &Path) -> IoResult<()> {
 /// the process lacks permissions to view the contents or if the `path` points
 /// at a non-directory file
 pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
-    let err = LocalIo::maybe_raise(|io| {
-        Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).into_iter().map(|a| {
-            Path::new(a)
-        }).collect())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't read directory",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::readdir(path)
+           .update_err("couldn't read directory",
+                       |e| format!("{}; path={}", e, path.display()))
 }
 
 /// Returns an iterator which will recursively walk the directory structure
@@ -666,8 +548,7 @@ pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
 pub fn walk_dir(path: &Path) -> IoResult<Directories> {
     Ok(Directories {
         stack: try!(readdir(path).update_err("couldn't walk directory",
-                                             |e| format!("{}; path={}",
-                                                         e, path.display())))
+                                             |e| format!("{}; path={}", e, path.display())))
     })
 }
 
@@ -681,12 +562,7 @@ impl Iterator<Path> for Directories {
         match self.stack.pop() {
             Some(path) => {
                 if path.is_dir() {
-                    let result = readdir(&path)
-                        .update_err("couldn't advance Directories iterator",
-                                    |e| format!("{}; path={}",
-                                                e, path.display()));
-
-                    match result {
+                    match readdir(&path) {
                         Ok(dirs) => { self.stack.extend(dirs.into_iter()); }
                         Err(..) => {}
                     }
@@ -804,11 +680,9 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
 /// be in milliseconds.
 // FIXME(#10301) these arguments should not be u64
 pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_utime(&path.to_c_str(), atime, mtime)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't change_file_times",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::utime(path, atime, mtime)
+           .update_err("couldn't change_file_times", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 impl Reader for File {
@@ -819,12 +693,11 @@ impl Reader for File {
                                           e, file.path.display()))
         }
 
-        let result = update_err(self.fd.read(buf)
-                                    .map_err(IoError::from_rtio_error), self);
+        let result = update_err(self.fd.read(buf), self);
 
         match result {
             Ok(read) => {
-                self.last_nread = read;
+                self.last_nread = read as int;
                 match read {
                     0 => update_err(Err(standard_error(io::EndOfFile)), self),
                     _ => Ok(read as uint)
@@ -837,32 +710,27 @@ impl Reader for File {
 
 impl Writer for File {
     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        let err = self.fd.write(buf).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't write to file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.write(buf)
+            .update_err("couldn't write to file",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 }
 
 impl Seek for File {
     fn tell(&self) -> IoResult<u64> {
-        let err = self.fd.tell().map_err(IoError::from_rtio_error);
-        err.update_err("couldn't retrieve file cursor (`tell`)",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.tell()
+            .update_err("couldn't retrieve file cursor (`tell`)",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 
     fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
-        let style = match style {
-            SeekSet => rtio::SeekSet,
-            SeekCur => rtio::SeekCur,
-            SeekEnd => rtio::SeekEnd,
-        };
         let err = match self.fd.seek(pos, style) {
             Ok(_) => {
                 // successful seek resets EOF indicator
                 self.last_nread = -1;
                 Ok(())
             }
-            Err(e) => Err(IoError::from_rtio_error(e)),
+            Err(e) => Err(e),
         };
         err.update_err("couldn't seek in file",
                        |e| format!("{}; path={}", e, self.path.display()))
@@ -942,6 +810,8 @@ fn access_string(access: FileAccess) -> &'static str {
 
 #[cfg(test)]
 #[allow(unused_imports)]
+#[allow(unused_variables)]
+#[allow(unused_mut)]
 mod test {
     use prelude::*;
     use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite};
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 78abbb9f80d..03c073c1477 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -316,6 +316,7 @@ impl IoError {
             err.detail = Some(os::error_string(errno).as_slice().chars()
                                  .map(|c| c.to_lowercase()).collect())
         }
+        err
     }
 
     /// Retrieve the last error to occur as a (detailed) IoError.
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index 7bae67c0aa6..98644cfc7e9 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -36,11 +36,11 @@ use kinds::Send;
 use libc;
 use option::{Option, Some, None};
 use boxed::Box;
+use sys::fs::FileDesc;
 use result::{Ok, Err};
 use rt;
 use rt::local::Local;
 use rt::task::Task;
-use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
 use slice::SlicePrelude;
 use str::StrPrelude;
 use uint;
@@ -75,14 +75,14 @@ use uint;
 //        case pipe also doesn't work, but magically file does!
 enum StdSource {
     TTY(Box<RtioTTY + Send>),
-    File(Box<RtioFileStream + Send>),
+    File(FileDesc),
 }
 
 fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
     LocalIo::maybe_raise(|io| {
         Ok(match io.tty_open(fd, readable) {
             Ok(tty) => f(TTY(tty)),
-            Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
+            Err(_) => f(File(FileDesc::new(fd, false))),
         })
     }).map_err(IoError::from_rtio_error).unwrap()
 }
@@ -278,10 +278,10 @@ impl Reader for StdReader {
                 // print!'d prompt not being shown until after the user hits
                 // enter.
                 flush();
-                tty.read(buf)
+                tty.read(buf).map_err(IoError::from_rtio_error)
             },
             File(ref mut file) => file.read(buf).map(|i| i as uint),
-        }.map_err(IoError::from_rtio_error);
+        };
         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
@@ -372,9 +372,9 @@ impl Writer for StdWriter {
         let max_size = if cfg!(windows) {8192} else {uint::MAX};
         for chunk in buf.chunks(max_size) {
             try!(match self.inner {
-                TTY(ref mut tty) => tty.write(chunk),
+                TTY(ref mut tty) => tty.write(chunk).map_err(IoError::from_rtio_error),
                 File(ref mut file) => file.write(chunk),
-            }.map_err(IoError::from_rtio_error))
+            })
         }
         Ok(())
     }
diff --git a/src/libstd/platform_imp/unix/fs.rs b/src/libstd/platform_imp/unix/fs.rs
new file mode 100644
index 00000000000..3dcd99859e8
--- /dev/null
+++ b/src/libstd/platform_imp/unix/fs.rs
@@ -0,0 +1,411 @@
+// Copyright 2013-2014 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
+
+use libc::{mod, c_int, c_void};
+use c_str::CString;
+use mem;
+use io;
+
+use prelude::*;
+
+use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
+use io::{IoResult, FileStat, SeekStyle, Reader};
+use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
+use result::{Ok, Err};
+use sys::retry;
+use sys_common::{keep_going, eof, mkerr_libc};
+
+pub use path::PosixPath as Path;
+
+pub type fd_t = libc::c_int;
+
+pub struct FileDesc {
+    /// The underlying C file descriptor.
+    fd: fd_t,
+
+    /// Whether to close the file descriptor on drop.
+    close_on_drop: bool,
+}
+
+impl FileDesc {
+    pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
+        FileDesc { fd: fd, close_on_drop: close_on_drop }
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> IoResult<uint> {
+        let ret = retry(|| unsafe {
+            libc::read(self.fd(),
+                       buf.as_mut_ptr() as *mut libc::c_void,
+                       buf.len() as libc::size_t)
+        });
+        if ret == 0 {
+            Err(eof())
+        } else if ret < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(ret as uint)
+        }
+    }
+    pub fn write(&self, buf: &[u8]) -> IoResult<()> {
+        let ret = keep_going(buf, |buf, len| {
+            unsafe {
+                libc::write(self.fd(), buf as *const libc::c_void,
+                            len as libc::size_t) as i64
+            }
+        });
+        if ret < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(())
+        }
+    }
+
+    pub fn fd(&self) -> fd_t { self.fd }
+
+    pub fn seek(&self, pos: i64, whence: SeekStyle) -> IoResult<u64> {
+        let whence = match whence {
+            SeekSet => libc::SEEK_SET,
+            SeekEnd => libc::SEEK_END,
+            SeekCur => libc::SEEK_CUR,
+        };
+        let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
+        if n < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(n as u64)
+        }
+    }
+
+    pub fn tell(&self) -> IoResult<u64> {
+        let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
+        if n < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(n as u64)
+        }
+    }
+
+    pub fn fsync(&self) -> IoResult<()> {
+        mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
+    }
+
+    pub fn datasync(&self) -> IoResult<()> {
+        return mkerr_libc(os_datasync(self.fd()));
+
+        #[cfg(any(target_os = "macos", target_os = "ios"))]
+        fn os_datasync(fd: c_int) -> c_int {
+            unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
+        }
+        #[cfg(target_os = "linux")]
+        fn os_datasync(fd: c_int) -> c_int {
+            retry(|| unsafe { libc::fdatasync(fd) })
+        }
+        #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))]
+        fn os_datasync(fd: c_int) -> c_int {
+            retry(|| unsafe { libc::fsync(fd) })
+        }
+    }
+
+    pub fn truncate(&self, offset: i64) -> IoResult<()> {
+        mkerr_libc(retry(|| unsafe {
+            libc::ftruncate(self.fd(), offset as libc::off_t)
+        }))
+    }
+
+    pub fn fstat(&self) -> IoResult<FileStat> {
+        let mut stat: libc::stat = unsafe { mem::zeroed() };
+        match unsafe { libc::fstat(self.fd(), &mut stat) } {
+            0 => Ok(mkstat(&stat)),
+            _ => Err(super::last_error()),
+        }
+    }
+
+    /// Extract the actual filedescriptor without closing it.
+    pub fn unwrap(self) -> fd_t {
+        let fd = self.fd;
+        unsafe { mem::forget(self) };
+        fd
+    }
+}
+
+/*
+
+impl RtioTTY for FileDesc {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner_read(buf)
+    }
+    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.inner_write(buf)
+    }
+    fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
+        Err(super::unimpl())
+    }
+    fn get_winsize(&mut self) -> IoResult<(int, int)> {
+        Err(super::unimpl())
+    }
+    fn isatty(&self) -> bool { false }
+}
+*/
+
+impl Drop for FileDesc {
+    fn drop(&mut self) {
+        // closing stdio file handles makes no sense, so never do it. Also, note
+        // that errors are ignored when closing a file descriptor. The reason
+        // for this is that if an error occurs we don't actually know if the
+        // file descriptor was closed or not, and if we retried (for something
+        // like EINTR), we might close another valid file descriptor (opened
+        // after we closed ours.
+        if self.close_on_drop && self.fd > libc::STDERR_FILENO {
+            let n = unsafe { libc::close(self.fd) };
+            if n != 0 {
+                println!("error {} when closing file descriptor {}", n, self.fd);
+            }
+        }
+    }
+}
+
+pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
+    let flags = match fm {
+        Open => 0,
+        Append => libc::O_APPEND,
+        Truncate => libc::O_TRUNC,
+    };
+    // Opening with a write permission must silently create the file.
+    let (flags, mode) = match fa {
+        Read => (flags | libc::O_RDONLY, 0),
+        Write => (flags | libc::O_WRONLY | libc::O_CREAT,
+                        libc::S_IRUSR | libc::S_IWUSR),
+        ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
+                            libc::S_IRUSR | libc::S_IWUSR),
+    };
+
+    let path = path.to_c_str();
+    match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
+        -1 => Err(super::last_error()),
+        fd => Ok(FileDesc::new(fd, true)),
+    }
+}
+
+pub fn mkdir(p: &Path, mode: uint) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
+}
+
+pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
+    use libc::{dirent_t};
+    use libc::{opendir, readdir_r, closedir};
+
+    fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
+        let root = unsafe { CString::new(root.as_ptr(), false) };
+        let root = Path::new(root);
+
+        dirs.into_iter().filter(|path| {
+            path.as_vec() != b"." && path.as_vec() != b".."
+        }).map(|path| root.join(path)).collect()
+    }
+
+    extern {
+        fn rust_dirent_t_size() -> libc::c_int;
+        fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char;
+    }
+
+    let size = unsafe { rust_dirent_t_size() };
+    let mut buf = Vec::<u8>::with_capacity(size as uint);
+    let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t;
+
+    let p = p.to_c_str();
+    let dir_ptr = unsafe {opendir(p.as_ptr())};
+
+    if dir_ptr as uint != 0 {
+        let mut paths = vec!();
+        let mut entry_ptr = 0 as *mut dirent_t;
+        while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
+            if entry_ptr.is_null() { break }
+            let cstr = unsafe {
+                CString::new(rust_list_dir_val(entry_ptr), false)
+            };
+            paths.push(Path::new(cstr));
+        }
+        assert_eq!(unsafe { closedir(dir_ptr) }, 0);
+        Ok(prune(&p, paths))
+    } else {
+        Err(super::last_error())
+    }
+}
+
+pub fn unlink(p: &Path) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
+}
+
+pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
+    let old = old.to_c_str();
+    let new = new.to_c_str();
+    mkerr_libc(unsafe {
+        libc::rename(old.as_ptr(), new.as_ptr())
+    })
+}
+
+pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(retry(|| unsafe {
+        libc::chmod(p.as_ptr(), mode as libc::mode_t)
+    }))
+}
+
+pub fn rmdir(p: &Path) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
+}
+
+pub fn chown(p: &Path, uid: int, gid: int) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(retry(|| unsafe {
+        libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t)
+    }))
+}
+
+pub fn readlink(p: &Path) -> IoResult<Path> {
+    let c_path = p.to_c_str();
+    let p = c_path.as_ptr();
+    let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
+    if len == -1 {
+        len = 1024; // FIXME: read PATH_MAX from C ffi?
+    }
+    let mut buf: Vec<u8> = Vec::with_capacity(len as uint);
+    match unsafe {
+        libc::readlink(p, buf.as_ptr() as *mut libc::c_char,
+                       len as libc::size_t) as libc::c_int
+    } {
+        -1 => Err(super::last_error()),
+        n => {
+            assert!(n > 0);
+            unsafe { buf.set_len(n as uint); }
+            Ok(Path::new(buf))
+        }
+    }
+}
+
+pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
+    let src = src.to_c_str();
+    let dst = dst.to_c_str();
+    mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
+}
+
+pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
+    let src = src.to_c_str();
+    let dst = dst.to_c_str();
+    mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
+}
+
+fn mkstat(stat: &libc::stat) -> FileStat {
+    // FileStat times are in milliseconds
+    fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
+
+    #[cfg(not(any(target_os = "linux", target_os = "android")))]
+    fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
+    #[cfg(any(target_os = "linux", target_os = "android"))]
+    fn flags(_stat: &libc::stat) -> u64 { 0 }
+
+    #[cfg(not(any(target_os = "linux", target_os = "android")))]
+    fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
+    #[cfg(any(target_os = "linux", target_os = "android"))]
+    fn gen(_stat: &libc::stat) -> u64 { 0 }
+
+    FileStat {
+        size: stat.st_size as u64,
+        kind: match (stat.st_mode as libc::mode_t) & libc::S_IFMT {
+            libc::S_IFREG => io::TypeFile,
+            libc::S_IFDIR => io::TypeDirectory,
+            libc::S_IFIFO => io::TypeNamedPipe,
+            libc::S_IFBLK => io::TypeBlockSpecial,
+            libc::S_IFLNK => io::TypeSymlink,
+            _ => io::TypeUnknown,
+        },
+        perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
+        created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
+        modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
+        accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
+        unstable: UnstableFileStat {
+            device: stat.st_dev as u64,
+            inode: stat.st_ino as u64,
+            rdev: stat.st_rdev as u64,
+            nlink: stat.st_nlink as u64,
+            uid: stat.st_uid as u64,
+            gid: stat.st_gid as u64,
+            blksize: stat.st_blksize as u64,
+            blocks: stat.st_blocks as u64,
+            flags: flags(stat),
+            gen: gen(stat),
+        },
+    }
+}
+
+pub fn stat(p: &Path) -> IoResult<FileStat> {
+    let p = p.to_c_str();
+    let mut stat: libc::stat = unsafe { mem::zeroed() };
+    match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
+        0 => Ok(mkstat(&stat)),
+        _ => Err(super::last_error()),
+    }
+}
+
+pub fn lstat(p: &Path) -> IoResult<FileStat> {
+    let p = p.to_c_str();
+    let mut stat: libc::stat = unsafe { mem::zeroed() };
+    match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
+        0 => Ok(mkstat(&stat)),
+        _ => Err(super::last_error()),
+    }
+}
+
+pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
+    let p = p.to_c_str();
+    let buf = libc::utimbuf {
+        actime: (atime / 1000) as libc::time_t,
+        modtime: (mtime / 1000) as libc::time_t,
+    };
+    mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::FileDesc;
+    use libc;
+    use os;
+    use prelude::*;
+
+    #[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer
+    #[test]
+    fn test_file_desc() {
+        // Run this test with some pipes so we don't have to mess around with
+        // opening or closing files.
+        let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
+        let mut reader = FileDesc::new(reader, true);
+        let mut writer = FileDesc::new(writer, true);
+
+        writer.write(b"test").ok().unwrap();
+        let mut buf = [0u8, ..4];
+        match reader.read(buf) {
+            Ok(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 => panic!("invalid read: {}", r),
+        }
+
+        assert!(writer.read(buf).is_err());
+        assert!(reader.write(buf).is_err());
+    }
+}
diff --git a/src/libnative/io/file_windows.rs b/src/libstd/platform_imp/windows/fs.rs
index eb4d4f22132..a07688b2fed 100644
--- a/src/libnative/io/file_windows.rs
+++ b/src/libstd/platform_imp/windows/fs.rs
@@ -1,4 +1,4 @@
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -12,42 +12,40 @@
 
 use alloc::arc::Arc;
 use libc::{mod, c_int};
-use std::c_str::CString;
-use std::mem;
-use std::os::windows::fill_utf16_buf_and_decode;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, IoError};
-use std::str;
 
-pub type fd_t = libc::c_int;
+use c_str::CString;
+use mem;
+use os::windows::fill_utf16_buf_and_decode;
+use path;
+use ptr;
+use str;
+use io;
 
-struct Inner {
-    fd: fd_t,
-    close_on_drop: bool,
-}
+use prelude::*;
+use sys;
+use sys_common::{keep_going, eof, mkerr_libc};
+
+use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
+use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
+use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
+
+pub use path::WindowsPath as Path;
+pub type fd_t = libc::c_int;
 
 pub struct FileDesc {
-    inner: Arc<Inner>
+    /// The underlying C file descriptor.
+    pub fd: fd_t,
+
+    /// Whether to close the file descriptor on drop.
+    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 { inner: Arc::new(Inner {
-            fd: fd,
-            close_on_drop: close_on_drop
-        }) }
+        FileDesc { fd: fd, close_on_drop: close_on_drop }
     }
 
-    pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+    pub fn read(&self, buf: &mut [u8]) -> IoResult<uint> {
         let mut read = 0;
         let ret = unsafe {
             libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
@@ -60,7 +58,8 @@ impl FileDesc {
             Err(super::last_error())
         }
     }
-    pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
+
+    pub fn write(&self, buf: &[u8]) -> IoResult<()> {
         let mut cur = buf.as_ptr();
         let mut remaining = buf.len();
         while remaining > 0 {
@@ -80,7 +79,7 @@ impl FileDesc {
         Ok(())
     }
 
-    pub fn fd(&self) -> fd_t { self.inner.fd }
+    pub fn fd(&self) -> fd_t { self.fd }
 
     pub fn handle(&self) -> libc::HANDLE {
         unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
@@ -88,153 +87,67 @@ impl FileDesc {
 
     // A version of seek that takes &self so that tell can call it
     //   - the private seek should of course take &mut self.
-    fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
+    fn seek_common(&self, pos: i64, style: SeekStyle) -> IoResult<u64> {
         let whence = match style {
-            rtio::SeekSet => libc::FILE_BEGIN,
-            rtio::SeekEnd => libc::FILE_END,
-            rtio::SeekCur => libc::FILE_CURRENT,
+            SeekSet => libc::FILE_BEGIN,
+            SeekEnd => libc::FILE_END,
+            SeekCur => libc::FILE_CURRENT,
         };
         unsafe {
             let mut newpos = 0;
-            match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
-                                         whence) {
+            match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, whence) {
                 0 => Err(super::last_error()),
                 _ => Ok(newpos as u64),
             }
         }
     }
 
-}
-
-impl rtio::RtioFileStream for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
-        self.inner_read(buf).map(|i| i as int)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
-        let mut read = 0;
-        let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
-        overlap.Offset = offset as libc::DWORD;
-        overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
-        let ret = unsafe {
-            libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
-                           buf.len() as libc::DWORD, &mut read,
-                           &mut overlap)
-        };
-        if ret != 0 {
-            Ok(read as int)
-        } else {
-            Err(super::last_error())
-        }
-    }
-    fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> {
-        let mut cur = buf.as_ptr();
-        let mut remaining = buf.len();
-        let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
-        while remaining > 0 {
-            overlap.Offset = offset as libc::DWORD;
-            overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
-            let mut amt = 0;
-            let ret = unsafe {
-                libc::WriteFile(self.handle(), cur as libc::LPVOID,
-                                remaining as libc::DWORD, &mut amt,
-                                &mut overlap)
-            };
-            if ret != 0 {
-                remaining -= amt as uint;
-                cur = unsafe { cur.offset(amt as int) };
-                offset += amt as u64;
-            } else {
-                return Err(super::last_error())
-            }
-        }
-        Ok(())
-    }
-
-    fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
+    pub fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<u64> {
         self.seek_common(pos, style)
     }
 
-    fn tell(&self) -> IoResult<u64> {
-        self.seek_common(0, rtio::SeekCur)
+    pub fn tell(&self) -> IoResult<u64> {
+        self.seek_common(0, SeekCur)
     }
 
-    fn fsync(&mut self) -> IoResult<()> {
+    pub fn fsync(&mut self) -> IoResult<()> {
         super::mkerr_winbool(unsafe {
             libc::FlushFileBuffers(self.handle())
         })
     }
 
-    fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
+    pub fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
 
-    fn truncate(&mut self, offset: i64) -> IoResult<()> {
+    pub fn truncate(&mut self, offset: i64) -> IoResult<()> {
         let orig_pos = try!(self.tell());
-        let _ = try!(self.seek(offset, rtio::SeekSet));
+        let _ = try!(self.seek(offset, SeekSet));
         let ret = unsafe {
             match libc::SetEndOfFile(self.handle()) {
                 0 => Err(super::last_error()),
                 _ => Ok(())
             }
         };
-        let _ = self.seek(orig_pos as i64, rtio::SeekSet);
+        let _ = self.seek(orig_pos as i64, SeekSet);
         return ret;
     }
 
-    fn fstat(&mut self) -> IoResult<rtio::FileStat> {
+    pub fn fstat(&mut self) -> IoResult<io::FileStat> {
         let mut stat: libc::stat = unsafe { mem::zeroed() };
         match unsafe { libc::fstat(self.fd(), &mut stat) } {
             0 => Ok(mkstat(&stat)),
             _ => Err(super::last_error()),
         }
     }
-}
 
-impl rtio::RtioPipe for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn clone(&self) -> Box<rtio::RtioPipe + Send> {
-        box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
-    }
-
-    // Only supported on named pipes currently. Note that this doesn't have an
-    // impact on the std::io primitives, this is never called via
-    // std::io::PipeStream. If the functionality is exposed in the future, then
-    // these methods will need to be implemented.
-    fn close_read(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn close_write(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn set_timeout(&mut self, _t: Option<u64>) {}
-    fn set_read_timeout(&mut self, _t: Option<u64>) {}
-    fn set_write_timeout(&mut self, _t: Option<u64>) {}
-}
-
-impl rtio::RtioTTY for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
+    /// Extract the actual filedescriptor without closing it.
+    pub fn unwrap(self) -> fd_t {
+        let fd = self.fd;
+        unsafe { mem::forget(self) };
+        fd
     }
-    fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn get_winsize(&mut self) -> IoResult<(int, int)> {
-        Err(super::unimpl())
-    }
-    fn isatty(&self) -> bool { false }
 }
 
-impl Drop for Inner {
+impl Drop for FileDesc {
     fn drop(&mut self) {
         // closing stdio file handles makes no sense, so never do it. Also, note
         // that errors are ignored when closing a file descriptor. The reason
@@ -251,39 +164,26 @@ impl Drop for Inner {
     }
 }
 
-pub fn to_utf16(s: &CString) -> IoResult<Vec<u16>> {
-    match s.as_str() {
-        Some(s) => Ok({
-            let mut s = s.utf16_units().collect::<Vec<u16>>();
-            s.push(0);
-            s
-        }),
-        None => Err(IoError {
-            code: libc::ERROR_INVALID_NAME as uint,
-            extra: 0,
-            detail: Some("valid unicode input required".to_string()),
-        })
-    }
+pub fn to_utf16(s: &Path) -> IoResult<Vec<u16>> {
+    sys::to_utf16(s.as_str())
 }
 
-pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
-        -> IoResult<FileDesc> {
+pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
     // Flags passed to open_osfhandle
     let flags = match fm {
-        rtio::Open => 0,
-        rtio::Append => libc::O_APPEND,
-        rtio::Truncate => libc::O_TRUNC,
+        Open => 0,
+        Append => libc::O_APPEND,
+        Truncate => libc::O_TRUNC,
     };
     let flags = match fa {
-        rtio::Read => flags | libc::O_RDONLY,
-        rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT,
-        rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
+        Read => flags | libc::O_RDONLY,
+        Write => flags | libc::O_WRONLY | libc::O_CREAT,
+        ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
     };
-
     let mut dwDesiredAccess = match fa {
-        rtio::Read => libc::FILE_GENERIC_READ,
-        rtio::Write => libc::FILE_GENERIC_WRITE,
-        rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
+        Read => libc::FILE_GENERIC_READ,
+        Write => libc::FILE_GENERIC_WRITE,
+        ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
     };
 
     // libuv has a good comment about this, but the basic idea is what we try to
@@ -293,15 +193,15 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
                       libc::FILE_SHARE_DELETE;
 
     let dwCreationDisposition = match (fm, fa) {
-        (rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING,
-        (rtio::Truncate, _) => libc::CREATE_ALWAYS,
-        (rtio::Open, rtio::Read) => libc::OPEN_EXISTING,
-        (rtio::Open, _) => libc::OPEN_ALWAYS,
-        (rtio::Append, rtio::Read) => {
+        (Truncate, Read) => libc::TRUNCATE_EXISTING,
+        (Truncate, _) => libc::CREATE_ALWAYS,
+        (Open, Read) => libc::OPEN_EXISTING,
+        (Open, _) => libc::OPEN_ALWAYS,
+        (Append, Read) => {
             dwDesiredAccess |= libc::FILE_APPEND_DATA;
             libc::OPEN_EXISTING
         }
-        (rtio::Append, _) => {
+        (Append, _) => {
             dwDesiredAccess &= !libc::FILE_WRITE_DATA;
             dwDesiredAccess |= libc::FILE_APPEND_DATA;
             libc::OPEN_ALWAYS
@@ -337,7 +237,7 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
     }
 }
 
-pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
+pub fn mkdir(p: &Path, _mode: uint) -> IoResult<()> {
     let p = try!(to_utf16(p));
     super::mkerr_winbool(unsafe {
         // FIXME: turn mode into something useful? #2623
@@ -345,20 +245,15 @@ pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
     })
 }
 
-pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
-    fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
-        let root = unsafe { CString::new(root.as_ptr(), false) };
-        let root = Path::new(root);
-
+pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
+    fn prune(root: &Path, dirs: Vec<Path>) -> Vec<Path> {
         dirs.into_iter().filter(|path| {
             path.as_vec() != b"." && path.as_vec() != b".."
-        }).map(|path| root.join(path).to_c_str()).collect()
+        }).map(|path| root.join(path)).collect()
     }
 
-    let star = Path::new(unsafe {
-        CString::new(p.as_ptr(), false)
-    }).join("*");
-    let path = try!(to_utf16(&star.to_c_str()));
+    let star = p.join("*");
+    let path = try!(to_utf16(&star));
 
     unsafe {
         let mut wfd = mem::zeroed();
@@ -374,8 +269,8 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
                         None => {
                             assert!(libc::FindClose(find_handle) != 0);
                             return Err(IoError {
-                                code: super::c::ERROR_ILLEGAL_CHARACTER as uint,
-                                extra: 0,
+                                kind: io::InvalidInput,
+                                desc: "path was not valid UTF-16",
                                 detail: Some(format!("path was not valid UTF-16: {}", filename)),
                             })
                         }, // FIXME #12056: Convert the UCS-2 to invalid utf-8 instead of erroring
@@ -391,42 +286,74 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
     }
 }
 
-pub fn unlink(p: &CString) -> IoResult<()> {
-    let p = try!(to_utf16(p));
-    super::mkerr_winbool(unsafe {
-        libc::DeleteFileW(p.as_ptr())
-    })
+pub fn unlink(p: &Path) -> IoResult<()> {
+    fn do_unlink(p_utf16: &Vec<u16>) -> IoResult<()> {
+        super::mkerr_winbool(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })
+    }
+
+    let p_utf16 = try!(to_utf16(p));
+    let res = do_unlink(&p_utf16);
+    match res {
+        Ok(()) => Ok(()),
+        Err(e) => {
+            // FIXME: change the code below to use more direct calls
+            // than `stat` and `chmod`, to avoid re-conversion to
+            // utf16 etc.
+
+            // On unix, a readonly file can be successfully removed. On windows,
+            // however, it cannot. To keep the two platforms in line with
+            // respect to their behavior, catch this case on windows, attempt to
+            // change it to read-write, and then remove the file.
+            if e.kind == io::PermissionDenied {
+                let stat = match stat(p) {
+                    Ok(stat) => stat,
+                    Err(..) => return Err(e),
+                };
+                if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
+
+                match chmod(p, (stat.perm | io::USER_WRITE).bits() as uint) {
+                    Ok(()) => do_unlink(&p_utf16),
+                    Err(..) => {
+                        // Try to put it back as we found it
+                        let _ = chmod(p, stat.perm.bits() as uint);
+                        Err(e)
+                    }
+                }
+            } else {
+                Err(e)
+            }
+        }
+    }
 }
 
-pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
+pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
     let old = try!(to_utf16(old));
     let new = try!(to_utf16(new));
     super::mkerr_winbool(unsafe {
-        libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
-                          libc::MOVEFILE_REPLACE_EXISTING)
+        libc::MoveFileExW(old.as_ptr(), new.as_ptr(), libc::MOVEFILE_REPLACE_EXISTING)
     })
 }
 
-pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
+pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
     let p = try!(to_utf16(p));
-    super::mkerr_libc(unsafe {
+    mkerr_libc(unsafe {
         libc::wchmod(p.as_ptr(), mode as libc::c_int)
     })
 }
 
-pub fn rmdir(p: &CString) -> IoResult<()> {
+pub fn rmdir(p: &Path) -> IoResult<()> {
     let p = try!(to_utf16(p));
-    super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
+    mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
 }
 
-pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
+pub fn chown(_p: &Path, _uid: int, _gid: int) -> IoResult<()> {
     // libuv has this as a no-op, so seems like this should as well?
     Ok(())
 }
 
-pub fn readlink(p: &CString) -> IoResult<CString> {
+pub fn readlink(p: &Path) -> IoResult<Path> {
     // FIXME: I have a feeling that this reads intermediate symlinks as well.
-    use io::c::compat::kernel32::GetFinalPathNameByHandleW;
+    use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
     let p = try!(to_utf16(p));
     let handle = unsafe {
         libc::CreateFileW(p.as_ptr(),
@@ -449,18 +376,18 @@ pub fn readlink(p: &CString) -> IoResult<CString> {
                                   libc::VOLUME_NAME_DOS)
     });
     let ret = match ret {
-        Some(ref s) if s.as_slice().starts_with(r"\\?\") => {
-            Ok(Path::new(s.as_slice().slice_from(4)).to_c_str())
+        Some(ref s) if s.as_slice().starts_with(r"\\?\") => { // "
+            Ok(Path::new(s.as_slice().slice_from(4)))
         }
-        Some(s) => Ok(Path::new(s).to_c_str()),
+        Some(s) => Ok(Path::new(s)),
         None => Err(super::last_error()),
     };
     assert!(unsafe { libc::CloseHandle(handle) } != 0);
     return ret;
 }
 
-pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
-    use io::c::compat::kernel32::CreateSymbolicLinkW;
+pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
+    use sys::c::compat::kernel32::CreateSymbolicLinkW;
     let src = try!(to_utf16(src));
     let dst = try!(to_utf16(dst));
     super::mkerr_winbool(unsafe {
@@ -468,7 +395,7 @@ pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
     })
 }
 
-pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
+pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
     let src = try!(to_utf16(src));
     let dst = try!(to_utf16(dst));
     super::mkerr_winbool(unsafe {
@@ -476,28 +403,37 @@ pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
     })
 }
 
-fn mkstat(stat: &libc::stat) -> rtio::FileStat {
-    rtio::FileStat {
+fn mkstat(stat: &libc::stat) -> FileStat {
+    FileStat {
         size: stat.st_size as u64,
-        kind: stat.st_mode as u64,
-        perm: stat.st_mode as u64,
+        kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT {
+            libc::S_IFREG => io::TypeFile,
+            libc::S_IFDIR => io::TypeDirectory,
+            libc::S_IFIFO => io::TypeNamedPipe,
+            libc::S_IFBLK => io::TypeBlockSpecial,
+            libc::S_IFLNK => io::TypeSymlink,
+            _ => io::TypeUnknown,
+        },
+        perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
         created: stat.st_ctime as u64,
         modified: stat.st_mtime as u64,
         accessed: stat.st_atime as u64,
-        device: stat.st_dev as u64,
-        inode: stat.st_ino as u64,
-        rdev: stat.st_rdev as u64,
-        nlink: stat.st_nlink as u64,
-        uid: stat.st_uid as u64,
-        gid: stat.st_gid as u64,
-        blksize: 0,
-        blocks: 0,
-        flags: 0,
-        gen: 0,
+        unstable: UnstableFileStat {
+            device: stat.st_dev as u64,
+            inode: stat.st_ino as u64,
+            rdev: stat.st_rdev as u64,
+            nlink: stat.st_nlink as u64,
+            uid: stat.st_uid as u64,
+            gid: stat.st_gid as u64,
+            blksize:0,
+            blocks: 0,
+            flags: 0,
+            gen: 0,
+        },
     }
 }
 
-pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
+pub fn stat(p: &Path) -> IoResult<FileStat> {
     let mut stat: libc::stat = unsafe { mem::zeroed() };
     let p = try!(to_utf16(p));
     match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
@@ -506,18 +442,19 @@ pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
     }
 }
 
-pub fn lstat(_p: &CString) -> IoResult<rtio::FileStat> {
+// FIXME: move this to platform-specific modules (for now)?
+pub fn lstat(_p: &Path) -> IoResult<FileStat> {
     // FIXME: implementation is missing
     Err(super::unimpl())
 }
 
-pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
+pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
     let mut buf = libc::utimbuf {
         actime: atime as libc::time64_t,
         modtime: mtime as libc::time64_t,
     };
     let p = try!(to_utf16(p));
-    super::mkerr_libc(unsafe {
+    mkerr_libc(unsafe {
         libc::wutime(p.as_ptr(), &mut buf)
     })
 }