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