about summary refs log tree commit diff
path: root/src/libstd
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 /src/libstd
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.
Diffstat (limited to 'src/libstd')
-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.rs460
5 files changed, 966 insertions, 224 deletions
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/libstd/platform_imp/windows/fs.rs b/src/libstd/platform_imp/windows/fs.rs
new file mode 100644
index 00000000000..a07688b2fed
--- /dev/null
+++ b/src/libstd/platform_imp/windows/fs.rs
@@ -0,0 +1,460 @@
+// 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.
+//
+// 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 Windows-based file I/O
+
+use alloc::arc::Arc;
+use libc::{mod, c_int};
+
+use c_str::CString;
+use mem;
+use os::windows::fill_utf16_buf_and_decode;
+use path;
+use ptr;
+use str;
+use io;
+
+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 {
+    /// The underlying C file descriptor.
+    pub 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 mut read = 0;
+        let ret = unsafe {
+            libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
+                           buf.len() as libc::DWORD, &mut read,
+                           ptr::null_mut())
+        };
+        if ret != 0 {
+            Ok(read as uint)
+        } else {
+            Err(super::last_error())
+        }
+    }
+
+    pub fn write(&self, buf: &[u8]) -> IoResult<()> {
+        let mut cur = buf.as_ptr();
+        let mut remaining = buf.len();
+        while remaining > 0 {
+            let mut amt = 0;
+            let ret = unsafe {
+                libc::WriteFile(self.handle(), cur as libc::LPVOID,
+                                remaining as libc::DWORD, &mut amt,
+                                ptr::null_mut())
+            };
+            if ret != 0 {
+                remaining -= amt as uint;
+                cur = unsafe { cur.offset(amt as int) };
+            } else {
+                return Err(super::last_error())
+            }
+        }
+        Ok(())
+    }
+
+    pub fn fd(&self) -> fd_t { self.fd }
+
+    pub fn handle(&self) -> libc::HANDLE {
+        unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
+    }
+
+    // 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: SeekStyle) -> IoResult<u64> {
+        let whence = match style {
+            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) {
+                0 => Err(super::last_error()),
+                _ => Ok(newpos as u64),
+            }
+        }
+    }
+
+    pub fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<u64> {
+        self.seek_common(pos, style)
+    }
+
+    pub fn tell(&self) -> IoResult<u64> {
+        self.seek_common(0, SeekCur)
+    }
+
+    pub fn fsync(&mut self) -> IoResult<()> {
+        super::mkerr_winbool(unsafe {
+            libc::FlushFileBuffers(self.handle())
+        })
+    }
+
+    pub fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
+
+    pub fn truncate(&mut self, offset: i64) -> IoResult<()> {
+        let orig_pos = try!(self.tell());
+        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, SeekSet);
+        return ret;
+    }
+
+    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()),
+        }
+    }
+
+    /// Extract the actual filedescriptor without closing it.
+    pub fn unwrap(self) -> fd_t {
+        let fd = self.fd;
+        unsafe { mem::forget(self) };
+        fd
+    }
+}
+
+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 to_utf16(s: &Path) -> IoResult<Vec<u16>> {
+    sys::to_utf16(s.as_str())
+}
+
+pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
+    // Flags passed to open_osfhandle
+    let flags = match fm {
+        Open => 0,
+        Append => libc::O_APPEND,
+        Truncate => libc::O_TRUNC,
+    };
+    let flags = match fa {
+        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 {
+        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
+    // emulate unix semantics by enabling all sharing by allowing things such as
+    // deleting a file while it's still open.
+    let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
+                      libc::FILE_SHARE_DELETE;
+
+    let dwCreationDisposition = match (fm, fa) {
+        (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
+        }
+        (Append, _) => {
+            dwDesiredAccess &= !libc::FILE_WRITE_DATA;
+            dwDesiredAccess |= libc::FILE_APPEND_DATA;
+            libc::OPEN_ALWAYS
+        }
+    };
+
+    let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
+    // Compat with unix, this allows opening directories (see libuv)
+    dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
+
+    let path = try!(to_utf16(path));
+    let handle = unsafe {
+        libc::CreateFileW(path.as_ptr(),
+                          dwDesiredAccess,
+                          dwShareMode,
+                          ptr::null_mut(),
+                          dwCreationDisposition,
+                          dwFlagsAndAttributes,
+                          ptr::null_mut())
+    };
+    if handle == libc::INVALID_HANDLE_VALUE {
+        Err(super::last_error())
+    } else {
+        let fd = unsafe {
+            libc::open_osfhandle(handle as libc::intptr_t, flags)
+        };
+        if fd < 0 {
+            let _ = unsafe { libc::CloseHandle(handle) };
+            Err(super::last_error())
+        } else {
+            Ok(FileDesc::new(fd, true))
+        }
+    }
+}
+
+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
+        libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
+    })
+}
+
+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)).collect()
+    }
+
+    let star = p.join("*");
+    let path = try!(to_utf16(&star));
+
+    unsafe {
+        let mut wfd = mem::zeroed();
+        let find_handle = libc::FindFirstFileW(path.as_ptr(), &mut wfd);
+        if find_handle != libc::INVALID_HANDLE_VALUE {
+            let mut paths = vec![];
+            let mut more_files = 1 as libc::BOOL;
+            while more_files != 0 {
+                {
+                    let filename = str::truncate_utf16_at_nul(wfd.cFileName);
+                    match String::from_utf16(filename) {
+                        Some(filename) => paths.push(Path::new(filename)),
+                        None => {
+                            assert!(libc::FindClose(find_handle) != 0);
+                            return Err(IoError {
+                                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
+                    }
+                }
+                more_files = libc::FindNextFileW(find_handle, &mut wfd);
+            }
+            assert!(libc::FindClose(find_handle) != 0);
+            Ok(prune(p, paths))
+        } else {
+            Err(super::last_error())
+        }
+    }
+}
+
+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: &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)
+    })
+}
+
+pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
+    let p = try!(to_utf16(p));
+    mkerr_libc(unsafe {
+        libc::wchmod(p.as_ptr(), mode as libc::c_int)
+    })
+}
+
+pub fn rmdir(p: &Path) -> IoResult<()> {
+    let p = try!(to_utf16(p));
+    mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
+}
+
+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: &Path) -> IoResult<Path> {
+    // FIXME: I have a feeling that this reads intermediate symlinks as well.
+    use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
+    let p = try!(to_utf16(p));
+    let handle = unsafe {
+        libc::CreateFileW(p.as_ptr(),
+                          libc::GENERIC_READ,
+                          libc::FILE_SHARE_READ,
+                          ptr::null_mut(),
+                          libc::OPEN_EXISTING,
+                          libc::FILE_ATTRIBUTE_NORMAL,
+                          ptr::null_mut())
+    };
+    if handle == libc::INVALID_HANDLE_VALUE {
+        return Err(super::last_error())
+    }
+    // Specify (sz - 1) because the documentation states that it's the size
+    // without the null pointer
+    let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
+        GetFinalPathNameByHandleW(handle,
+                                  buf as *const u16,
+                                  sz - 1,
+                                  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)))
+        }
+        Some(s) => Ok(Path::new(s)),
+        None => Err(super::last_error()),
+    };
+    assert!(unsafe { libc::CloseHandle(handle) } != 0);
+    return ret;
+}
+
+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 {
+        CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
+    })
+}
+
+pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
+    let src = try!(to_utf16(src));
+    let dst = try!(to_utf16(dst));
+    super::mkerr_winbool(unsafe {
+        libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
+    })
+}
+
+fn mkstat(stat: &libc::stat) -> FileStat {
+    FileStat {
+        size: stat.st_size 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,
+        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: &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) } {
+        0 => Ok(mkstat(&stat)),
+        _ => Err(super::last_error()),
+    }
+}
+
+// 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: &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));
+    mkerr_libc(unsafe {
+        libc::wutime(p.as_ptr(), &mut buf)
+    })
+}