about summary refs log tree commit diff
path: root/src/libstd/io/native
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-11-19 10:56:42 -0800
committerbors <bors@rust-lang.org>2013-11-19 10:56:42 -0800
commiteef913b290f668b4f131ead5be65a1615616426b (patch)
tree9be070be09e2754b700922454567f6e334d24882 /src/libstd/io/native
parentd57765d8a9d5c1136b914d0d3a073d44882f4ba9 (diff)
parent68d5510292ed2f9714568285759ca9ef54d9b48c (diff)
downloadrust-eef913b290f668b4f131ead5be65a1615616426b.tar.gz
rust-eef913b290f668b4f131ead5be65a1615616426b.zip
auto merge of #10495 : alexcrichton/rust/more-native-io, r=brson
This implements a fair amount of the unimpl() functionality in io::native
relating to filesystem operations. I've also modified all io::fs tests to run in
both a native and uv environment (so everything is actually tested).

There are a few bits of remaining functionality which I was unable to get
working:

* truncate on windows
* change_file_times on windows
* lstat on windows

I think that change_file_times may just need a better interface, but the other
two have large implementations in libuv which I didn't want to tackle trying to
copy. I found a `chsize` function to work for truncate on windows, but it
doesn't quite seem to be working out.
Diffstat (limited to 'src/libstd/io/native')
-rw-r--r--src/libstd/io/native/file.rs1090
-rw-r--r--src/libstd/io/native/mod.rs90
2 files changed, 695 insertions, 485 deletions
diff --git a/src/libstd/io/native/file.rs b/src/libstd/io/native/file.rs
index c157efa7d38..b2cb8f735cf 100644
--- a/src/libstd/io/native/file.rs
+++ b/src/libstd/io/native/file.rs
@@ -12,16 +12,26 @@
 
 #[allow(non_camel_case_types)];
 
+use c_str::CString;
 use io::IoError;
 use io;
+use libc::c_int;
 use libc;
 use ops::Drop;
 use option::{Some, None, Option};
 use os;
+use path::{Path, GenericPath};
 use ptr::RawPtr;
 use result::{Result, Ok, Err};
 use rt::rtio;
+use super::IoResult;
+use unstable::intrinsics;
 use vec::ImmutableVector;
+use vec;
+
+#[cfg(windows)] use os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
+#[cfg(windows)] use ptr;
+#[cfg(windows)] use str;
 
 fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 {
     #[cfg(windows)] static eintr: int = 0; // doesn't matter
@@ -122,26 +132,157 @@ impl rtio::RtioFileStream for FileDesc {
     fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
         self.inner_write(buf)
     }
-    fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result<int, IoError> {
-        Err(super::unimpl())
+    fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
+        return os_pread(self.fd, vec::raw::to_ptr(buf), buf.len(), offset);
+
+        #[cfg(windows)]
+        fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<int> {
+            unsafe {
+                let mut overlap: libc::OVERLAPPED = intrinsics::init();
+                let handle = libc::get_osfhandle(fd) as libc::HANDLE;
+                let mut bytes_read = 0;
+                overlap.Offset = offset as libc::DWORD;
+                overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
+
+                match libc::ReadFile(handle, buf as libc::LPVOID,
+                                     amt as libc::DWORD,
+                                     &mut bytes_read, &mut overlap) {
+                    0 => Err(super::last_error()),
+                    _ => Ok(bytes_read as int)
+                }
+            }
+        }
+
+        #[cfg(unix)]
+        fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<int> {
+            match unsafe {
+                libc::pread(fd, buf as *libc::c_void, amt 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) -> Result<(), IoError> {
-        Err(super::unimpl())
+    fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
+        return os_pwrite(self.fd, vec::raw::to_ptr(buf), buf.len(), offset);
+
+        #[cfg(windows)]
+        fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> {
+            unsafe {
+                let mut overlap: libc::OVERLAPPED = intrinsics::init();
+                let handle = libc::get_osfhandle(fd) as libc::HANDLE;
+                overlap.Offset = offset as libc::DWORD;
+                overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
+
+                match libc::WriteFile(handle, buf as libc::LPVOID,
+                                      amt as libc::DWORD,
+                                      ptr::mut_null(), &mut overlap) {
+                    0 => Err(super::last_error()),
+                    _ => Ok(()),
+                }
+            }
+        }
+
+        #[cfg(unix)]
+        fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> {
+            super::mkerr_libc(unsafe {
+                libc::pwrite(fd, buf as *libc::c_void, amt as libc::size_t,
+                             offset as libc::off_t)
+            } as c_int)
+        }
     }
-    fn seek(&mut self, _pos: i64, _whence: io::SeekStyle) -> Result<u64, IoError> {
-        Err(super::unimpl())
+    #[cfg(windows)]
+    fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> {
+        let whence = match style {
+            io::SeekSet => libc::FILE_BEGIN,
+            io::SeekEnd => libc::FILE_END,
+            io::SeekCur => libc::FILE_CURRENT,
+        };
+        unsafe {
+            let handle = libc::get_osfhandle(self.fd) as libc::HANDLE;
+            let mut newpos = 0;
+            match libc::SetFilePointerEx(handle, pos, &mut newpos, whence) {
+                0 => Err(super::last_error()),
+                _ => Ok(newpos as u64),
+            }
+        }
+    }
+    #[cfg(unix)]
+    fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result<u64, IoError> {
+        let whence = match whence {
+            io::SeekSet => libc::SEEK_SET,
+            io::SeekEnd => libc::SEEK_END,
+            io::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) -> Result<u64, IoError> {
-        Err(super::unimpl())
+        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) -> Result<(), IoError> {
-        Err(super::unimpl())
+        return os_fsync(self.fd);
+
+        #[cfg(windows)]
+        fn os_fsync(fd: c_int) -> IoResult<()> {
+            super::mkerr_winbool(unsafe {
+                let handle = libc::get_osfhandle(fd);
+                libc::FlushFileBuffers(handle as libc::HANDLE)
+            })
+        }
+        #[cfg(unix)]
+        fn os_fsync(fd: c_int) -> IoResult<()> {
+            super::mkerr_libc(unsafe { libc::fsync(fd) })
+        }
     }
+    #[cfg(windows)]
+    fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); }
+
+    #[cfg(not(windows))]
     fn datasync(&mut self) -> Result<(), IoError> {
-        Err(super::unimpl())
+        return super::mkerr_libc(os_datasync(self.fd));
+
+        #[cfg(target_os = "macos")]
+        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 { unsafe { libc::fdatasync(fd) } }
+        #[cfg(not(target_os = "macos"), not(target_os = "linux"))]
+        fn os_datasync(fd: c_int) -> c_int { unsafe { libc::fsync(fd) } }
     }
-    fn truncate(&mut self, _offset: i64) -> Result<(), IoError> {
-        Err(super::unimpl())
+
+    #[cfg(windows)]
+    fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
+        let orig_pos = match self.tell() { Ok(i) => i, Err(e) => return Err(e) };
+        match self.seek(offset, io::SeekSet) {
+            Ok(_) => {}, Err(e) => return Err(e),
+        };
+        let ret = unsafe {
+            let handle = libc::get_osfhandle(self.fd) as libc::HANDLE;
+            match libc::SetEndOfFile(handle) {
+                0 => Err(super::last_error()),
+                _ => Ok(())
+            }
+        };
+        self.seek(orig_pos as i64, io::SeekSet);
+        return ret;
+    }
+    #[cfg(unix)]
+    fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
+        super::mkerr_libc(unsafe {
+            libc::ftruncate(self.fd, offset as libc::off_t)
+        })
     }
 }
 
@@ -180,7 +321,8 @@ impl Drop for FileDesc {
 }
 
 pub struct CFile {
-    priv file: *libc::FILE
+    priv file: *libc::FILE,
+    priv fd: FileDesc,
 }
 
 impl CFile {
@@ -188,7 +330,16 @@ impl CFile {
     ///
     /// The `CFile` takes ownership of the `FILE` pointer and will close it upon
     /// destruction.
-    pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } }
+    pub fn new(file: *libc::FILE) -> CFile {
+        CFile {
+            file: file,
+            fd: FileDesc::new(unsafe { libc::fileno(file) }, false)
+        }
+    }
+
+    pub fn flush(&mut self) -> Result<(), IoError> {
+        super::mkerr_libc(unsafe { libc::fflush(self.file) })
+    }
 }
 
 impl rtio::RtioFileStream for CFile {
@@ -222,11 +373,13 @@ impl rtio::RtioFileStream for CFile {
         }
     }
 
-    fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result<int, IoError> {
-        Err(super::unimpl())
+    fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
+        self.flush();
+        self.fd.pread(buf, offset)
     }
-    fn pwrite(&mut self, _buf: &[u8], _offset: u64) -> Result<(), IoError> {
-        Err(super::unimpl())
+    fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
+        self.flush();
+        self.fd.pwrite(buf, offset)
     }
     fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> {
         let whence = match style {
@@ -250,13 +403,16 @@ impl rtio::RtioFileStream for CFile {
         }
     }
     fn fsync(&mut self) -> Result<(), IoError> {
-        Err(super::unimpl())
+        self.flush();
+        self.fd.fsync()
     }
     fn datasync(&mut self) -> Result<(), IoError> {
-        Err(super::unimpl())
+        self.flush();
+        self.fd.fsync()
     }
-    fn truncate(&mut self, _offset: i64) -> Result<(), IoError> {
-        Err(super::unimpl())
+    fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
+        self.flush();
+        self.fd.truncate(offset)
     }
 }
 
@@ -266,529 +422,561 @@ impl Drop for CFile {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use libc;
-    use os;
-    use io::{io_error, SeekSet, Writer, Reader};
-    use result::Ok;
-    use super::{CFile, FileDesc};
+pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
+        -> IoResult<FileDesc> {
+    let flags = match fm {
+        io::Open => 0,
+        io::Append => libc::O_APPEND,
+        io::Truncate => libc::O_TRUNC,
+    };
+    // Opening with a write permission must silently create the file.
+    let (flags, mode) = match fa {
+        io::Read => (flags | libc::O_RDONLY, 0),
+        io::Write => (flags | libc::O_WRONLY | libc::O_CREAT,
+                      libc::S_IRUSR | libc::S_IWUSR),
+        io::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
+                          libc::S_IRUSR | libc::S_IWUSR),
+    };
+
+    return match os_open(path, flags, mode) {
+        -1 => Err(super::last_error()),
+        fd => Ok(FileDesc::new(fd, true)),
+    };
 
-    #[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.inner_write(bytes!("test"));
-            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 => fail!("invalid read: {:?}", r)
-            }
-
-            assert!(writer.inner_read(buf).is_err());
-            assert!(reader.inner_write(buf).is_err());
+    #[cfg(windows)]
+    fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int {
+        do as_utf16_p(path.as_str().unwrap()) |path| {
+            unsafe { libc::wopen(path, flags, mode) }
         }
     }
 
-    #[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);
+    #[cfg(unix)]
+    fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int {
+        unsafe { libc::open(path.with_ref(|p| p), flags, mode) }
+    }
+}
 
-            file.write(bytes!("test"));
-            let mut buf = [0u8, ..4];
-            file.seek(0, SeekSet);
-            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 => fail!("invalid read: {:?}", r)
+pub fn mkdir(p: &CString, mode: io::FilePermission) -> IoResult<()> {
+    return os_mkdir(p, mode as c_int);
+
+    #[cfg(windows)]
+    fn os_mkdir(p: &CString, _mode: c_int) -> IoResult<()> {
+        super::mkerr_winbool(unsafe {
+            // FIXME: turn mode into something useful? #2623
+            do as_utf16_p(p.as_str().unwrap()) |buf| {
+                libc::CreateDirectoryW(buf, ptr::mut_null())
             }
-        }
+        })
+    }
+
+    #[cfg(unix)]
+    fn os_mkdir(p: &CString, mode: c_int) -> IoResult<()> {
+        super::mkerr_libc(unsafe {
+            libc::mkdir(p.with_ref(|p| p), mode as libc::mode_t)
+        })
     }
 }
 
-// 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;
+pub fn readdir(p: &CString) -> IoResult<~[Path]> {
+    fn prune(root: &CString, dirs: ~[Path]) -> ~[Path] {
+        let root = unsafe { CString::new(root.with_ref(|p| p), false) };
+        let root = Path::new(root);
 
-    #[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;
+        dirs.move_iter().filter(|path| {
+            path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
+        }).map(|path| root.join(path)).collect()
+    }
 
-    // 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.
+    unsafe {
+        #[cfg(not(windows))]
+        unsafe fn get_list(p: &CString) -> IoResult<~[Path]> {
+            use libc::{dirent_t};
+            use libc::{opendir, readdir, closedir};
+            extern {
+                fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char;
+            }
+            debug!("os::list_dir -- BEFORE OPENDIR");
 
-    mod rustrt {
-        use libc::{c_char, c_int};
-        use libc;
+            let dir_ptr = do p.with_ref |buf| {
+                opendir(buf)
+            };
 
-        extern {
-            pub fn rust_path_is_dir(path: *libc::c_char) -> c_int;
-            pub fn rust_path_exists(path: *libc::c_char) -> c_int;
+            if (dir_ptr as uint != 0) {
+                let mut paths = ~[];
+                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);
+                Ok(paths)
+            } else {
+                Err(super::last_error())
+            }
         }
 
-        // 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;
+        unsafe fn get_list(p: &CString) -> IoResult<~[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 p = CString::new(p.with_ref(|p| p), false);
+            let p = Path::new(p);
+            let star = p.join("*");
+            do as_utf16_p(star.as_str().unwrap()) |path_ptr| {
+                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 paths = ~[];
+                    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);
+                    Ok(paths)
+                } else {
+                    Err(super::last_error())
+                }
+            }
         }
+
+        get_list(p).map(|paths| prune(p, paths))
     }
+}
+
+pub fn unlink(p: &CString) -> IoResult<()> {
+    return os_unlink(p);
 
-    /// 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(windows)]
+    fn os_unlink(p: &CString) -> IoResult<()> {
+        super::mkerr_winbool(unsafe {
+            do as_utf16_p(p.as_str().unwrap()) |buf| {
+                libc::DeleteFileW(buf)
+            }
         })
     }
 
     #[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
+    fn os_unlink(p: &CString) -> IoResult<()> {
+        super::mkerr_libc(unsafe { libc::unlink(p.with_ref(|p| p)) })
+    }
+}
+
+pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
+    return os_rename(old, new);
+
+    #[cfg(windows)]
+    fn os_rename(old: &CString, new: &CString) -> IoResult<()> {
+        super::mkerr_winbool(unsafe {
+            do as_utf16_p(old.as_str().unwrap()) |old| {
+                do as_utf16_p(new.as_str().unwrap()) |new| {
+                    libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING)
+                }
             }
-        }
+        })
+    }
+
+    #[cfg(unix)]
+    fn os_rename(old: &CString, new: &CString) -> IoResult<()> {
+        super::mkerr_libc(unsafe {
+            libc::rename(old.with_ref(|p| p), new.with_ref(|p| p))
+        })
     }
+}
 
+pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> {
+    return super::mkerr_libc(os_chmod(p, mode as c_int));
 
     #[cfg(windows)]
-    pub fn path_is_dir(p: &Path) -> bool {
+    fn os_chmod(p: &CString, mode: c_int) -> c_int {
         unsafe {
-            do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| {
-                rustrt::rust_path_is_dir_u16(buf) != 0 as c_int
+            do as_utf16_p(p.as_str().unwrap()) |p| {
+                libc::wchmod(p, mode)
             }
         }
     }
 
     #[cfg(unix)]
-    /// Indicates whether a path exists
-    pub fn path_exists(p: &Path) -> bool {
+    fn os_chmod(p: &CString, mode: c_int) -> c_int {
+        unsafe { libc::chmod(p.with_ref(|p| p), mode as libc::mode_t) }
+    }
+}
+
+pub fn rmdir(p: &CString) -> IoResult<()> {
+    return super::mkerr_libc(os_rmdir(p));
+
+    #[cfg(windows)]
+    fn os_rmdir(p: &CString) -> c_int {
         unsafe {
-            do p.with_c_str |buf| {
-                rustrt::rust_path_exists(buf) != 0 as c_int
-            }
+            do as_utf16_p(p.as_str().unwrap()) |p| { libc::wrmdir(p) }
         }
     }
 
+    #[cfg(unix)]
+    fn os_rmdir(p: &CString) -> c_int {
+        unsafe { libc::rmdir(p.with_ref(|p| p)) }
+    }
+}
+
+pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> {
+    return super::mkerr_libc(os_chown(p, uid, gid));
+
+    // libuv has this as a no-op, so seems like this should as well?
     #[cfg(windows)]
-    pub fn path_exists(p: &Path) -> bool {
+    fn os_chown(_p: &CString, _uid: int, _gid: int) -> c_int { 0 }
+
+    #[cfg(unix)]
+    fn os_chown(p: &CString, uid: int, gid: int) -> c_int {
         unsafe {
-            do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| {
-                rustrt::rust_path_exists_u16(buf) != 0 as c_int
-            }
+            libc::chown(p.with_ref(|p| p), uid as libc::uid_t,
+                        gid as libc::gid_t)
         }
     }
+}
 
-    /// Creates a directory at the specified path
-    pub fn make_dir(p: &Path, mode: c_int) -> bool {
-        return mkdir(p, mode);
+pub fn readlink(p: &CString) -> IoResult<Path> {
+    return os_readlink(p);
 
-        #[cfg(windows)]
-        fn mkdir(p: &Path, _mode: c_int) -> bool {
+    // XXX: I have a feeling that this reads intermediate symlinks as well.
+    #[cfg(windows)]
+    fn os_readlink(p: &CString) -> IoResult<Path> {
+        let handle = unsafe {
+            do as_utf16_p(p.as_str().unwrap()) |p| {
+                libc::CreateFileW(p,
+                                  libc::GENERIC_READ,
+                                  libc::FILE_SHARE_READ,
+                                  ptr::mut_null(),
+                                  libc::OPEN_EXISTING,
+                                  libc::FILE_ATTRIBUTE_NORMAL,
+                                  ptr::mut_null())
+            }
+        };
+        if handle == ptr::mut_null() { return Err(super::last_error()) }
+        let ret = do fill_utf16_buf_and_decode |buf, sz| {
             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)
-                }
+                libc::GetFinalPathNameByHandleW(handle, buf as *u16, sz,
+                                                libc::VOLUME_NAME_NT)
             }
-        }
+        };
+        let ret = match ret {
+            Some(s) => Ok(Path::new(s)),
+            None => Err(super::last_error()),
+        };
+        unsafe { libc::CloseHandle(handle) };
+        return ret;
 
-        #[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;
+    #[cfg(unix)]
+    fn os_readlink(p: &CString) -> IoResult<Path> {
+        let p = p.with_ref(|p| p);
+        let mut len = unsafe { libc::pathconf(p, libc::_PC_NAME_MAX) };
+        if len == -1 {
+            len = 1024; // XXX: read PATH_MAX from C ffi?
         }
-        if p.filename().is_some() {
-            let mut p_ = p.clone();
-            p_.pop();
-            if !mkdir_recursive(&p_, mode) {
-                return false;
+        let mut buf = vec::with_capacity::<u8>(len as uint);
+        match unsafe {
+            libc::readlink(p, vec::raw::to_ptr(buf) as *mut libc::c_char,
+                           len as libc::size_t)
+        } {
+            -1 => Err(super::last_error()),
+            n => {
+                assert!(n > 0);
+                unsafe { vec::raw::set_len(&mut buf, n as uint); }
+                Ok(Path::new(buf))
             }
         }
-        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()
-        }
-    }
+pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
+    return os_symlink(src, dst);
 
-    /// 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;
-                    }
-                }
+    #[cfg(windows)]
+    fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> {
+        super::mkerr_winbool(do as_utf16_p(src.as_str().unwrap()) |src| {
+            do as_utf16_p(dst.as_str().unwrap()) |dst| {
+                unsafe { libc::CreateSymbolicLinkW(dst, src, 0) }
             }
-            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(unix)]
+    fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> {
+        super::mkerr_libc(unsafe {
+            libc::symlink(src.with_ref(|p| p), dst.with_ref(|p| 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)
-                };
-            }
-        }
+pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
+    return os_link(src, dst);
 
-        #[cfg(unix)]
-        fn rmdir(p: &Path) -> bool {
-            do p.with_c_str |buf| {
-                unsafe {
-                    libc::rmdir(buf) == (0 as c_int)
-                }
+    #[cfg(windows)]
+    fn os_link(src: &CString, dst: &CString) -> IoResult<()> {
+        super::mkerr_winbool(do as_utf16_p(src.as_str().unwrap()) |src| {
+            do as_utf16_p(dst.as_str().unwrap()) |dst| {
+                unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) }
             }
-        }
+        })
     }
 
-    /// Deletes an existing file
-    pub fn remove_file(p: &Path) -> bool {
-        return unlink(p);
+    #[cfg(unix)]
+    fn os_link(src: &CString, dst: &CString) -> IoResult<()> {
+        super::mkerr_libc(unsafe {
+            libc::link(src.with_ref(|p| p), dst.with_ref(|p| 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)
-                }
-            }
+#[cfg(windows)]
+fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
+    let path = unsafe { CString::new(path.with_ref(|p| p), false) };
+
+    // FileStat times are in milliseconds
+    fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
+
+    let kind = match (stat.st_mode as 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,
+    };
+
+    io::FileStat {
+        path: Path::new(path),
+        size: stat.st_size as u64,
+        kind: kind,
+        perm: (stat.st_mode) as io::FilePermission & io::AllPermissions,
+        created: stat.st_ctime as u64,
+        modified: stat.st_mtime as u64,
+        accessed: stat.st_atime as u64,
+        unstable: io::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,
         }
     }
+}
 
-    /// 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)
-                }
-           }
+#[cfg(unix)]
+fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
+    let path = unsafe { CString::new(path.with_ref(|p| p), false) };
+
+    // FileStat times are in milliseconds
+    fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
+
+    let kind = match (stat.st_mode as 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,
+    };
+
+    #[cfg(not(target_os = "linux"), not(target_os = "android"))]
+    fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
+    #[cfg(target_os = "linux")] #[cfg(target_os = "android")]
+    fn flags(_stat: &libc::stat) -> u64 { 0 }
+
+    #[cfg(not(target_os = "linux"), not(target_os = "android"))]
+    fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
+    #[cfg(target_os = "linux")] #[cfg(target_os = "android")]
+    fn gen(_stat: &libc::stat) -> u64 { 0 }
+
+    io::FileStat {
+        path: Path::new(path),
+        size: stat.st_size as u64,
+        kind: kind,
+        perm: (stat.st_mode) as io::FilePermission & io::AllPermissions,
+        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: io::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),
         }
     }
+}
 
-    /// Copies a file from one location to another
-    pub fn copy_file(from: &Path, to: &Path) -> bool {
-        return do_copy_file(from, to);
+pub fn stat(p: &CString) -> IoResult<io::FileStat> {
+    return os_stat(p);
 
-        #[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(windows)]
+    fn os_stat(p: &CString) -> IoResult<io::FileStat> {
+        let mut stat: libc::stat = unsafe { intrinsics::uninit() };
+        do as_utf16_p(p.as_str().unwrap()) |up| {
+            match unsafe { libc::wstat(up, &mut stat) } {
+                0 => Ok(mkstat(&stat, p)),
+                _ => Err(super::last_error()),
             }
         }
+    }
 
-        #[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;
-            }
+    #[cfg(unix)]
+    fn os_stat(p: &CString) -> IoResult<io::FileStat> {
+        let mut stat: libc::stat = unsafe { intrinsics::uninit() };
+        match unsafe { libc::stat(p.with_ref(|p| p), &mut stat) } {
+            0 => Ok(mkstat(&stat, p)),
+            _ => Err(super::last_error()),
         }
     }
+}
 
-    #[test]
-    fn tmpdir() {
-        let p = os::tmpdir();
-        let s = p.as_str();
-        assert!(s.is_some() && s.unwrap() != ".");
-    }
+pub fn lstat(p: &CString) -> IoResult<io::FileStat> {
+    return os_lstat(p);
 
-    // Issue #712
-    #[test]
-    fn test_list_dir_no_invalid_memory_access() {
-        list_dir(&Path::new("."));
+    // XXX: windows implementation is missing
+    #[cfg(windows)]
+    fn os_lstat(_p: &CString) -> IoResult<io::FileStat> {
+        Err(super::unimpl())
     }
 
-    #[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());
+    #[cfg(unix)]
+    fn os_lstat(p: &CString) -> IoResult<io::FileStat> {
+        let mut stat: libc::stat = unsafe { intrinsics::uninit() };
+        match unsafe { libc::lstat(p.with_ref(|p| p), &mut stat) } {
+            0 => Ok(mkstat(&stat, p)),
+            _ => Err(super::last_error()),
         }
     }
+}
+
+pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
+    return super::mkerr_libc(os_utime(p, atime, mtime));
 
-    #[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);
+    fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int {
+        let buf = libc::utimbuf {
+            actime: (atime / 1000) as libc::time64_t,
+            modtime: (mtime / 1000) as libc::time64_t,
+        };
+        unsafe {
+            do as_utf16_p(p.as_str().unwrap()) |p| {
+                libc::wutime(p, &buf)
+            }
+        }
     }
 
-    #[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());
+    #[cfg(unix)]
+    fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int {
+        let buf = libc::utimbuf {
+            actime: (atime / 1000) as libc::time_t,
+            modtime: (mtime / 1000) as libc::time_t,
+        };
+        unsafe { libc::utime(p.with_ref(|p| p), &buf) }
+    }
+}
 
-        mkdir_recursive(&dirpath, UserRWX);
+#[cfg(test)]
+mod tests {
+    use io::native::file::{CFile, FileDesc};
+    use io::fs;
+    use io;
+    use libc;
+    use os;
+    use path::Path;
+    use rand;
+    use result::Ok;
+    use rt::rtio::RtioFileStream;
 
-        assert!((path_is_dir(&dirpath)));
+    fn tmpdir() -> Path {
+        let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>()));
+        fs::mkdir(&ret, io::UserRWX);
+        ret
+    }
 
-        let mut filepath = dirpath;
-        filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs");
-        debug!("path_is_dir filepath: {}", filepath.display());
+    #[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);
 
-        File::create(&filepath); // ignore return; touch only
-        assert!((!path_is_dir(&filepath)));
+            writer.inner_write(bytes!("test"));
+            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 => fail!("invalid read: {:?}", r)
+            }
 
-        assert!((!path_is_dir(&Path::new(
-                     "test/unicode-bogus-dir-\uac00\u4e00\u30fc\u4f60\u597d"))));
+            assert!(writer.inner_read(buf).is_err());
+            assert!(reader.inner_write(buf).is_err());
+        }
     }
 
-    #[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>())); // 각丁ー再见
+    #[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);
 
-        mkdir_recursive(&dirpath, UserRWX);
-        assert!((path_exists(&dirpath)));
-        assert!((!path_exists(&Path::new(
-                     "test/unicode-bogus-path-\uac01\u4e01\u30fc\u518d\u89c1"))));
+            file.write(bytes!("test"));
+            let mut buf = [0u8, ..4];
+            file.seek(0, io::SeekSet);
+            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 => fail!("invalid read: {:?}", r)
+            }
+        }
     }
 }
diff --git a/src/libstd/io/native/mod.rs b/src/libstd/io/native/mod.rs
index d7aa6c14fcd..cec0de00ec2 100644
--- a/src/libstd/io/native/mod.rs
+++ b/src/libstd/io/native/mod.rs
@@ -90,6 +90,24 @@ fn last_error() -> IoError {
     }
 }
 
+// unix has nonzero values as errors
+fn mkerr_libc(ret: libc::c_int) -> IoResult<()> {
+    if ret != 0 {
+        Err(last_error())
+    } else {
+        Ok(())
+    }
+}
+
+// windows has zero values as errors
+fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
+    if ret == 0 {
+        Err(last_error())
+    } else {
+        Ok(())
+    }
+}
+
 /// Implementation of rt::rtio's IoFactory trait to generate handles to the
 /// native I/O functionality.
 pub struct IoFactory;
@@ -125,51 +143,51 @@ impl rtio::IoFactory for IoFactory {
         };
         ~file::FileDesc::new(fd, close) as ~RtioFileStream
     }
-    fn fs_open(&mut self, _path: &CString, _fm: io::FileMode, _fa: io::FileAccess)
+    fn fs_open(&mut self, path: &CString, fm: io::FileMode, fa: io::FileAccess)
         -> IoResult<~RtioFileStream> {
-        Err(unimpl())
+        file::open(path, fm, fa).map(|fd| ~fd as ~RtioFileStream)
     }
-    fn fs_unlink(&mut self, _path: &CString) -> IoResult<()> {
-        Err(unimpl())
+    fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
+        file::unlink(path)
     }
-    fn fs_stat(&mut self, _path: &CString) -> IoResult<io::FileStat> {
-        Err(unimpl())
+    fn fs_stat(&mut self, path: &CString) -> IoResult<io::FileStat> {
+        file::stat(path)
     }
-    fn fs_mkdir(&mut self, _path: &CString,
-                _mode: io::FilePermission) -> IoResult<()> {
-        Err(unimpl())
+    fn fs_mkdir(&mut self, path: &CString,
+                mode: io::FilePermission) -> IoResult<()> {
+        file::mkdir(path, mode)
     }
-    fn fs_chmod(&mut self, _path: &CString,
-                _mode: io::FilePermission) -> IoResult<()> {
-        Err(unimpl())
+    fn fs_chmod(&mut self, path: &CString,
+                mode: io::FilePermission) -> IoResult<()> {
+        file::chmod(path, mode)
     }
-    fn fs_rmdir(&mut self, _path: &CString) -> IoResult<()> {
-        Err(unimpl())
+    fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
+        file::rmdir(path)
     }
-    fn fs_rename(&mut self, _path: &CString, _to: &CString) -> IoResult<()> {
-        Err(unimpl())
+    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<~[Path]> {
-        Err(unimpl())
+    fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<~[Path]> {
+        file::readdir(path)
     }
-    fn fs_lstat(&mut self, _path: &CString) -> IoResult<io::FileStat> {
-        Err(unimpl())
+    fn fs_lstat(&mut self, path: &CString) -> IoResult<io::FileStat> {
+        file::lstat(path)
     }
-    fn fs_chown(&mut self, _path: &CString, _uid: int, _gid: int) -> IoResult<()> {
-        Err(unimpl())
+    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<Path> {
-        Err(unimpl())
+    fn fs_readlink(&mut self, path: &CString) -> IoResult<Path> {
+        file::readlink(path)
     }
-    fn fs_symlink(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> {
-        Err(unimpl())
+    fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
+        file::symlink(src, dst)
     }
-    fn fs_link(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> {
-        Err(unimpl())
+    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<()> {
-        Err(unimpl())
+    fn fs_utime(&mut self, src: &CString, atime: u64,
+                mtime: u64) -> IoResult<()> {
+        file::utime(src, atime, mtime)
     }
 
     // misc
@@ -183,14 +201,18 @@ impl rtio::IoFactory for IoFactory {
              io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect())
         })
     }
-    fn pipe_open(&mut self, _fd: c_int) -> IoResult<~RtioPipe> {
-        Err(unimpl())
+    fn pipe_open(&mut self, fd: c_int) -> IoResult<~RtioPipe> {
+        Ok(~file::FileDesc::new(fd, true) as ~RtioPipe)
     }
     fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> {
         if unsafe { libc::isatty(fd) } != 0 {
             Ok(~file::FileDesc::new(fd, true) as ~RtioTTY)
         } else {
-            Err(unimpl())
+            Err(IoError {
+                kind: io::MismatchedFileTypeForOperation,
+                desc: "file descriptor is not a TTY",
+                detail: None,
+            })
         }
     }
     fn signal(&mut self, _signal: Signum, _channel: SharedChan<Signum>)