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
@ -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;
|
||||
|
||||
// Local re-exports
|
||||
pub use self::file::FileDesc;
|
||||
pub use self::process::Process;
|
||||
|
||||
mod helper_thread;
|
||||
@ -41,13 +40,6 @@ pub mod net;
|
||||
pub mod process;
|
||||
mod util;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "file_unix.rs"]
|
||||
pub mod file;
|
||||
#[cfg(windows)]
|
||||
#[path = "file_windows.rs"]
|
||||
pub mod file;
|
||||
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
@ -92,25 +84,6 @@ fn last_error() -> IoError {
|
||||
}
|
||||
}
|
||||
|
||||
// unix has nonzero values as errors
|
||||
fn mkerr_libc <Int: num::Zero>(ret: Int) -> IoResult<()> {
|
||||
if !ret.is_zero() {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// windows has zero values as errors
|
||||
#[cfg(windows)]
|
||||
fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
|
||||
if ret == 0 {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[inline]
|
||||
fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
|
||||
@ -199,62 +172,6 @@ impl rtio::IoFactory for IoFactory {
|
||||
addrinfo::GetAddrInfoRequest::run(host, servname, hint)
|
||||
}
|
||||
|
||||
// filesystem operations
|
||||
fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior)
|
||||
-> Box<rtio::RtioFileStream + Send> {
|
||||
let close = match close {
|
||||
rtio::CloseSynchronously | rtio::CloseAsynchronously => true,
|
||||
rtio::DontClose => false
|
||||
};
|
||||
box file::FileDesc::new(fd, close) as Box<rtio::RtioFileStream + Send>
|
||||
}
|
||||
fn fs_open(&mut self, path: &CString, fm: rtio::FileMode,
|
||||
fa: rtio::FileAccess)
|
||||
-> IoResult<Box<rtio::RtioFileStream + Send>>
|
||||
{
|
||||
file::open(path, fm, fa).map(|fd| box fd as Box<rtio::RtioFileStream + Send>)
|
||||
}
|
||||
fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
|
||||
file::unlink(path)
|
||||
}
|
||||
fn fs_stat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
|
||||
file::stat(path)
|
||||
}
|
||||
fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()> {
|
||||
file::mkdir(path, mode)
|
||||
}
|
||||
fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()> {
|
||||
file::chmod(path, mode)
|
||||
}
|
||||
fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
|
||||
file::rmdir(path)
|
||||
}
|
||||
fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> {
|
||||
file::rename(path, to)
|
||||
}
|
||||
fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<Vec<CString>> {
|
||||
file::readdir(path)
|
||||
}
|
||||
fn fs_lstat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
|
||||
file::lstat(path)
|
||||
}
|
||||
fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> {
|
||||
file::chown(path, uid, gid)
|
||||
}
|
||||
fn fs_readlink(&mut self, path: &CString) -> IoResult<CString> {
|
||||
file::readlink(path)
|
||||
}
|
||||
fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
|
||||
file::symlink(src, dst)
|
||||
}
|
||||
fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
|
||||
file::link(src, dst)
|
||||
}
|
||||
fn fs_utime(&mut self, src: &CString, atime: u64,
|
||||
mtime: u64) -> IoResult<()> {
|
||||
file::utime(src, atime, mtime)
|
||||
}
|
||||
|
||||
// misc
|
||||
fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
|
||||
timer::Timer::new().map(|t| box t as Box<rtio::RtioTimer + Send>)
|
||||
|
@ -56,7 +56,7 @@ use std::sync::atomic;
|
||||
use std::comm;
|
||||
|
||||
use io::c;
|
||||
use io::file::FileDesc;
|
||||
use platform_imp::fs::FileDesc;
|
||||
use io::helper_thread::Helper;
|
||||
|
||||
helper_init!(static HELPER: Helper<Req>)
|
||||
|
@ -50,20 +50,6 @@ pub trait RemoteCallback {
|
||||
fn fire(&mut self);
|
||||
}
|
||||
|
||||
/// Description of what to do when a file handle is closed
|
||||
pub enum CloseBehavior {
|
||||
/// Do not close this handle when the object is destroyed
|
||||
DontClose,
|
||||
/// Synchronously close the handle, meaning that the task will block when
|
||||
/// the handle is destroyed until it has been fully closed.
|
||||
CloseSynchronously,
|
||||
/// Asynchronously closes a handle, meaning that the task will *not* block
|
||||
/// when the handle is destroyed, but the handle will still get deallocated
|
||||
/// and cleaned up (but this will happen asynchronously on the local event
|
||||
/// loop).
|
||||
CloseAsynchronously,
|
||||
}
|
||||
|
||||
/// Data needed to spawn a process. Serializes the `std::io::process::Command`
|
||||
/// builder.
|
||||
pub struct ProcessConfig<'a> {
|
||||
@ -202,28 +188,6 @@ pub trait IoFactory {
|
||||
hint: Option<AddrinfoHint>)
|
||||
-> IoResult<Vec<AddrinfoInfo>>;
|
||||
|
||||
// filesystem operations
|
||||
fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior)
|
||||
-> Box<RtioFileStream + Send>;
|
||||
fn fs_open(&mut self, path: &CString, fm: FileMode, fa: FileAccess)
|
||||
-> IoResult<Box<RtioFileStream + Send>>;
|
||||
fn fs_unlink(&mut self, path: &CString) -> IoResult<()>;
|
||||
fn fs_stat(&mut self, path: &CString) -> IoResult<FileStat>;
|
||||
fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()>;
|
||||
fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()>;
|
||||
fn fs_rmdir(&mut self, path: &CString) -> IoResult<()>;
|
||||
fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()>;
|
||||
fn fs_readdir(&mut self, path: &CString, flags: c_int) ->
|
||||
IoResult<Vec<CString>>;
|
||||
fn fs_lstat(&mut self, path: &CString) -> IoResult<FileStat>;
|
||||
fn fs_chown(&mut self, path: &CString, uid: int, gid: int) ->
|
||||
IoResult<()>;
|
||||
fn fs_readlink(&mut self, path: &CString) -> IoResult<CString>;
|
||||
fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
|
||||
fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
|
||||
fn fs_utime(&mut self, src: &CString, atime: u64, mtime: u64) ->
|
||||
IoResult<()>;
|
||||
|
||||
// misc
|
||||
fn timer_init(&mut self) -> IoResult<Box<RtioTimer + Send>>;
|
||||
fn spawn(&mut self, cfg: ProcessConfig)
|
||||
@ -296,19 +260,6 @@ pub trait RtioTimer {
|
||||
fn period(&mut self, msecs: u64, cb: Box<Callback + Send>);
|
||||
}
|
||||
|
||||
pub trait RtioFileStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<int>;
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()>;
|
||||
fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int>;
|
||||
fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()>;
|
||||
fn seek(&mut self, pos: i64, whence: SeekStyle) -> IoResult<u64>;
|
||||
fn tell(&self) -> IoResult<u64>;
|
||||
fn fsync(&mut self) -> IoResult<()>;
|
||||
fn datasync(&mut self) -> IoResult<()>;
|
||||
fn truncate(&mut self, offset: i64) -> IoResult<()>;
|
||||
fn fstat(&mut self) -> IoResult<FileStat>;
|
||||
}
|
||||
|
||||
pub trait RtioProcess {
|
||||
fn id(&self) -> libc::pid_t;
|
||||
fn kill(&mut self, signal: int) -> IoResult<()>;
|
||||
@ -399,43 +350,6 @@ pub enum ProcessExit {
|
||||
ExitSignal(int),
|
||||
}
|
||||
|
||||
pub enum FileMode {
|
||||
Open,
|
||||
Append,
|
||||
Truncate,
|
||||
}
|
||||
|
||||
pub enum FileAccess {
|
||||
Read,
|
||||
Write,
|
||||
ReadWrite,
|
||||
}
|
||||
|
||||
pub struct FileStat {
|
||||
pub size: u64,
|
||||
pub kind: u64,
|
||||
pub perm: u64,
|
||||
pub created: u64,
|
||||
pub modified: u64,
|
||||
pub accessed: u64,
|
||||
pub device: u64,
|
||||
pub inode: u64,
|
||||
pub rdev: u64,
|
||||
pub nlink: u64,
|
||||
pub uid: u64,
|
||||
pub gid: u64,
|
||||
pub blksize: u64,
|
||||
pub blocks: u64,
|
||||
pub flags: u64,
|
||||
pub gen: u64,
|
||||
}
|
||||
|
||||
pub enum SeekStyle {
|
||||
SeekSet,
|
||||
SeekEnd,
|
||||
SeekCur,
|
||||
}
|
||||
|
||||
pub struct AddrinfoHint {
|
||||
pub family: uint,
|
||||
pub socktype: uint,
|
||||
|
@ -52,28 +52,25 @@ fs::unlink(&path);
|
||||
|
||||
*/
|
||||
|
||||
use c_str::ToCStr;
|
||||
use clone::Clone;
|
||||
use io::standard_error;
|
||||
use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
||||
use io::{FilePermission, Write, Open, FileAccess, FileMode};
|
||||
use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
|
||||
use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
||||
use io::{Read, Truncate, ReadWrite, Append};
|
||||
use io::UpdateIoError;
|
||||
use io;
|
||||
use iter::{Iterator, Extend};
|
||||
use kinds::Send;
|
||||
use libc;
|
||||
use option::{Some, None, Option};
|
||||
use boxed::Box;
|
||||
use path::{Path, GenericPath};
|
||||
use path;
|
||||
use result::{Err, Ok};
|
||||
use rt::rtio::LocalIo;
|
||||
use rt::rtio;
|
||||
use slice::SlicePrelude;
|
||||
use string::String;
|
||||
use vec::Vec;
|
||||
|
||||
use sys::fs as fs_imp;
|
||||
use sys_common;
|
||||
|
||||
/// Unconstrained file access type that exposes read and write operations
|
||||
///
|
||||
/// Can be constructed via `File::open()`, `File::create()`, and
|
||||
@ -86,11 +83,17 @@ use vec::Vec;
|
||||
/// configured at creation time, via the `FileAccess` parameter to
|
||||
/// `File::open_mode()`.
|
||||
pub struct File {
|
||||
fd: Box<rtio::RtioFileStream + Send>,
|
||||
fd: fs_imp::FileDesc,
|
||||
path: Path,
|
||||
last_nread: int,
|
||||
}
|
||||
|
||||
impl sys_common::AsFileDesc for File {
|
||||
fn as_fd(&self) -> &fs_imp::FileDesc {
|
||||
&self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Open a file at `path` in the mode specified by the `mode` and `access`
|
||||
/// arguments
|
||||
@ -133,26 +136,13 @@ impl File {
|
||||
pub fn open_mode(path: &Path,
|
||||
mode: FileMode,
|
||||
access: FileAccess) -> IoResult<File> {
|
||||
let rtio_mode = match mode {
|
||||
Open => rtio::Open,
|
||||
Append => rtio::Append,
|
||||
Truncate => rtio::Truncate,
|
||||
};
|
||||
let rtio_access = match access {
|
||||
Read => rtio::Read,
|
||||
Write => rtio::Write,
|
||||
ReadWrite => rtio::ReadWrite,
|
||||
};
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_open(&path.to_c_str(), rtio_mode, rtio_access).map(|fd| {
|
||||
File {
|
||||
path: path.clone(),
|
||||
fd: fd,
|
||||
last_nread: -1
|
||||
}
|
||||
})
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't open file", |e| {
|
||||
fs_imp::open(path, mode, access).map(|fd| {
|
||||
File {
|
||||
path: path.clone(),
|
||||
fd: fd,
|
||||
last_nread: -1
|
||||
}
|
||||
}).update_err("couldn't open file", |e| {
|
||||
format!("{}; path={}; mode={}; access={}", e, path.display(),
|
||||
mode_string(mode), access_string(access))
|
||||
})
|
||||
@ -194,7 +184,7 @@ impl File {
|
||||
/// ```
|
||||
pub fn create(path: &Path) -> IoResult<File> {
|
||||
File::open_mode(path, Truncate, Write)
|
||||
.update_desc("couldn't create file")
|
||||
.update_desc("couldn't create file")
|
||||
}
|
||||
|
||||
/// Returns the original path which was used to open this file.
|
||||
@ -206,9 +196,9 @@ impl File {
|
||||
/// device. This will flush any internal buffers necessary to perform this
|
||||
/// operation.
|
||||
pub fn fsync(&mut self) -> IoResult<()> {
|
||||
let err = self.fd.fsync().map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't fsync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.fsync()
|
||||
.update_err("couldn't fsync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
|
||||
/// This function is similar to `fsync`, except that it may not synchronize
|
||||
@ -216,9 +206,9 @@ impl File {
|
||||
/// must synchronize content, but don't need the metadata on disk. The goal
|
||||
/// of this method is to reduce disk operations.
|
||||
pub fn datasync(&mut self) -> IoResult<()> {
|
||||
let err = self.fd.datasync().map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't datasync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.datasync()
|
||||
.update_err("couldn't datasync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
|
||||
/// Either truncates or extends the underlying file, updating the size of
|
||||
@ -230,10 +220,9 @@ impl File {
|
||||
/// will be extended to `size` and have all of the intermediate data filled
|
||||
/// in with 0s.
|
||||
pub fn truncate(&mut self, size: i64) -> IoResult<()> {
|
||||
let err = self.fd.truncate(size).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't truncate file", |e| {
|
||||
format!("{}; path={}; size={}", e, self.path.display(), size)
|
||||
})
|
||||
self.fd.truncate(size)
|
||||
.update_err("couldn't truncate file", |e|
|
||||
format!("{}; path={}; size={}", e, self.path.display(), size))
|
||||
}
|
||||
|
||||
/// Returns true if the stream has reached the end of the file.
|
||||
@ -251,12 +240,9 @@ impl File {
|
||||
|
||||
/// Queries information about the underlying file.
|
||||
pub fn stat(&mut self) -> IoResult<FileStat> {
|
||||
let err = match self.fd.fstat() {
|
||||
Ok(s) => Ok(from_rtio(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
};
|
||||
err.update_err("couldn't fstat file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.fstat()
|
||||
.update_err("couldn't fstat file", |e|
|
||||
format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,41 +268,9 @@ impl File {
|
||||
/// user lacks permissions to remove the file, or if some other filesystem-level
|
||||
/// error occurs.
|
||||
pub fn unlink(path: &Path) -> IoResult<()> {
|
||||
return match do_unlink(path) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
// On unix, a readonly file can be successfully removed. On windows,
|
||||
// however, it cannot. To keep the two platforms in line with
|
||||
// respect to their behavior, catch this case on windows, attempt to
|
||||
// change it to read-write, and then remove the file.
|
||||
if cfg!(windows) && e.kind == io::PermissionDenied {
|
||||
let stat = match stat(path) {
|
||||
Ok(stat) => stat,
|
||||
Err(..) => return Err(e),
|
||||
};
|
||||
if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
|
||||
|
||||
match chmod(path, stat.perm | io::USER_WRITE) {
|
||||
Ok(()) => do_unlink(path),
|
||||
Err(..) => {
|
||||
// Try to put it back as we found it
|
||||
let _ = chmod(path, stat.perm);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn do_unlink(path: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_unlink(&path.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't unlink path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
fs_imp::unlink(path)
|
||||
.update_err("couldn't unlink path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Given a path, query the file system to get information about a file,
|
||||
@ -341,12 +295,9 @@ pub fn unlink(path: &Path) -> IoResult<()> {
|
||||
/// to perform a `stat` call on the given `path` or if there is no entry in the
|
||||
/// filesystem at the provided path.
|
||||
pub fn stat(path: &Path) -> IoResult<FileStat> {
|
||||
let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) {
|
||||
Ok(s) => Ok(from_rtio(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
};
|
||||
err.update_err("couldn't stat path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::stat(path)
|
||||
.update_err("couldn't stat path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Perform the same operation as the `stat` function, except that this
|
||||
@ -358,53 +309,9 @@ pub fn stat(path: &Path) -> IoResult<FileStat> {
|
||||
///
|
||||
/// See `stat`
|
||||
pub fn lstat(path: &Path) -> IoResult<FileStat> {
|
||||
let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) {
|
||||
Ok(s) => Ok(from_rtio(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
};
|
||||
err.update_err("couldn't lstat path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
fn from_rtio(s: rtio::FileStat) -> FileStat {
|
||||
#[cfg(windows)]
|
||||
type Mode = libc::c_int;
|
||||
#[cfg(unix)]
|
||||
type Mode = libc::mode_t;
|
||||
|
||||
let rtio::FileStat {
|
||||
size, kind, perm, created, modified,
|
||||
accessed, device, inode, rdev,
|
||||
nlink, uid, gid, blksize, blocks, flags, gen
|
||||
} = s;
|
||||
|
||||
FileStat {
|
||||
size: size,
|
||||
kind: match (kind as Mode) & libc::S_IFMT {
|
||||
libc::S_IFREG => io::TypeFile,
|
||||
libc::S_IFDIR => io::TypeDirectory,
|
||||
libc::S_IFIFO => io::TypeNamedPipe,
|
||||
libc::S_IFBLK => io::TypeBlockSpecial,
|
||||
libc::S_IFLNK => io::TypeSymlink,
|
||||
_ => io::TypeUnknown,
|
||||
},
|
||||
perm: FilePermission::from_bits_truncate(perm as u32),
|
||||
created: created,
|
||||
modified: modified,
|
||||
accessed: accessed,
|
||||
unstable: UnstableFileStat {
|
||||
device: device,
|
||||
inode: inode,
|
||||
rdev: rdev,
|
||||
nlink: nlink,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
blksize: blksize,
|
||||
blocks: blocks,
|
||||
flags: flags,
|
||||
gen: gen,
|
||||
},
|
||||
}
|
||||
fs_imp::lstat(path)
|
||||
.update_err("couldn't lstat path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Rename a file or directory to a new name.
|
||||
@ -424,12 +331,9 @@ fn from_rtio(s: rtio::FileStat) -> FileStat {
|
||||
/// the process lacks permissions to view the contents, or if some other
|
||||
/// intermittent I/O error occurs.
|
||||
pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_rename(&from.to_c_str(), &to.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't rename path", |e| {
|
||||
format!("{}; from={}; to={}", e, from.display(), to.display())
|
||||
})
|
||||
fs_imp::rename(from, to)
|
||||
.update_err("couldn't rename path", |e|
|
||||
format!("{}; from={}; to={}", e, from.display(), to.display()))
|
||||
}
|
||||
|
||||
/// Copies the contents of one file to another. This function will also
|
||||
@ -462,8 +366,9 @@ pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
|
||||
/// being created and then destroyed by this operation.
|
||||
pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
||||
fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> {
|
||||
result.update_err("couldn't copy path",
|
||||
|e| format!("{}; from={}; to={}", e, from.display(), to.display()))
|
||||
result.update_err("couldn't copy path", |e| {
|
||||
format!("{}; from={}; to={}", e, from.display(), to.display())
|
||||
})
|
||||
}
|
||||
|
||||
if !from.is_file() {
|
||||
@ -512,45 +417,33 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
||||
/// the process lacks permissions to change the attributes of the file, or if
|
||||
/// some other I/O error is encountered.
|
||||
pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_chmod(&path.to_c_str(), mode.bits() as uint)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't chmod path", |e| {
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode)
|
||||
})
|
||||
fs_imp::chmod(path, mode.bits() as uint)
|
||||
.update_err("couldn't chmod path", |e|
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode))
|
||||
}
|
||||
|
||||
/// Change the user and group owners of a file at the specified path.
|
||||
pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_chown(&path.to_c_str(), uid, gid)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't chown path", |e| {
|
||||
format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)
|
||||
})
|
||||
fs_imp::chown(path, uid, gid)
|
||||
.update_err("couldn't chown path", |e|
|
||||
format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid))
|
||||
}
|
||||
|
||||
/// Creates a new hard link on the filesystem. The `dst` path will be a
|
||||
/// link pointing to the `src` path. Note that systems often require these
|
||||
/// two paths to both be located on the same filesystem.
|
||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_link(&src.to_c_str(), &dst.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't link path", |e| {
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display())
|
||||
})
|
||||
fs_imp::link(src, dst)
|
||||
.update_err("couldn't link path", |e|
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display()))
|
||||
}
|
||||
|
||||
/// Creates a new symbolic link on the filesystem. The `dst` path will be a
|
||||
/// symlink pointing to the `src` path.
|
||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_symlink(&src.to_c_str(), &dst.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't symlink path", |e| {
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display())
|
||||
})
|
||||
fs_imp::symlink(src, dst)
|
||||
.update_err("couldn't symlink path", |e|
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display()))
|
||||
}
|
||||
|
||||
/// Reads a symlink, returning the file that the symlink points to.
|
||||
@ -560,11 +453,9 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
/// This function will return an error on failure. Failure conditions include
|
||||
/// reading a file that does not exist or reading a file which is not a symlink.
|
||||
pub fn readlink(path: &Path) -> IoResult<Path> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
Ok(Path::new(try!(io.fs_readlink(&path.to_c_str()))))
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't resolve symlink for path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::readlink(path)
|
||||
.update_err("couldn't resolve symlink for path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Create a new, empty directory at the provided path
|
||||
@ -585,12 +476,9 @@ pub fn readlink(path: &Path) -> IoResult<Path> {
|
||||
/// This function will return an error if the user lacks permissions to make a
|
||||
/// new directory at the provided `path`, or if the directory already exists.
|
||||
pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_mkdir(&path.to_c_str(), mode.bits() as uint)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't create directory", |e| {
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode)
|
||||
})
|
||||
fs_imp::mkdir(path, mode.bits() as uint)
|
||||
.update_err("couldn't create directory", |e|
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode))
|
||||
}
|
||||
|
||||
/// Remove an existing, empty directory
|
||||
@ -610,11 +498,9 @@ pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
|
||||
/// This function will return an error if the user lacks permissions to remove
|
||||
/// the directory at the provided `path`, or if the directory isn't empty.
|
||||
pub fn rmdir(path: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_rmdir(&path.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't remove directory",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::rmdir(path)
|
||||
.update_err("couldn't remove directory", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Retrieve a vector containing all entries within a provided directory
|
||||
@ -650,13 +536,9 @@ pub fn rmdir(path: &Path) -> IoResult<()> {
|
||||
/// the process lacks permissions to view the contents or if the `path` points
|
||||
/// at a non-directory file
|
||||
pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).into_iter().map(|a| {
|
||||
Path::new(a)
|
||||
}).collect())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't read directory",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::readdir(path)
|
||||
.update_err("couldn't read directory",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Returns an iterator which will recursively walk the directory structure
|
||||
@ -666,8 +548,7 @@ pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
|
||||
pub fn walk_dir(path: &Path) -> IoResult<Directories> {
|
||||
Ok(Directories {
|
||||
stack: try!(readdir(path).update_err("couldn't walk directory",
|
||||
|e| format!("{}; path={}",
|
||||
e, path.display())))
|
||||
|e| format!("{}; path={}", e, path.display())))
|
||||
})
|
||||
}
|
||||
|
||||
@ -681,12 +562,7 @@ impl Iterator<Path> for Directories {
|
||||
match self.stack.pop() {
|
||||
Some(path) => {
|
||||
if path.is_dir() {
|
||||
let result = readdir(&path)
|
||||
.update_err("couldn't advance Directories iterator",
|
||||
|e| format!("{}; path={}",
|
||||
e, path.display()));
|
||||
|
||||
match result {
|
||||
match readdir(&path) {
|
||||
Ok(dirs) => { self.stack.extend(dirs.into_iter()); }
|
||||
Err(..) => {}
|
||||
}
|
||||
@ -804,11 +680,9 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
|
||||
/// be in milliseconds.
|
||||
// FIXME(#10301) these arguments should not be u64
|
||||
pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_utime(&path.to_c_str(), atime, mtime)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't change_file_times",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::utime(path, atime, mtime)
|
||||
.update_err("couldn't change_file_times", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
impl Reader for File {
|
||||
@ -819,12 +693,11 @@ impl Reader for File {
|
||||
e, file.path.display()))
|
||||
}
|
||||
|
||||
let result = update_err(self.fd.read(buf)
|
||||
.map_err(IoError::from_rtio_error), self);
|
||||
let result = update_err(self.fd.read(buf), self);
|
||||
|
||||
match result {
|
||||
Ok(read) => {
|
||||
self.last_nread = read;
|
||||
self.last_nread = read as int;
|
||||
match read {
|
||||
0 => update_err(Err(standard_error(io::EndOfFile)), self),
|
||||
_ => Ok(read as uint)
|
||||
@ -837,32 +710,27 @@ impl Reader for File {
|
||||
|
||||
impl Writer for File {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
let err = self.fd.write(buf).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't write to file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.write(buf)
|
||||
.update_err("couldn't write to file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for File {
|
||||
fn tell(&self) -> IoResult<u64> {
|
||||
let err = self.fd.tell().map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't retrieve file cursor (`tell`)",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.tell()
|
||||
.update_err("couldn't retrieve file cursor (`tell`)",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
||||
let style = match style {
|
||||
SeekSet => rtio::SeekSet,
|
||||
SeekCur => rtio::SeekCur,
|
||||
SeekEnd => rtio::SeekEnd,
|
||||
};
|
||||
let err = match self.fd.seek(pos, style) {
|
||||
Ok(_) => {
|
||||
// successful seek resets EOF indicator
|
||||
self.last_nread = -1;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
err.update_err("couldn't seek in file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
@ -942,6 +810,8 @@ fn access_string(access: FileAccess) -> &'static str {
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(unused_imports)]
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_mut)]
|
||||
mod test {
|
||||
use prelude::*;
|
||||
use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite};
|
||||
|
@ -316,6 +316,7 @@ impl IoError {
|
||||
err.detail = Some(os::error_string(errno).as_slice().chars()
|
||||
.map(|c| c.to_lowercase()).collect())
|
||||
}
|
||||
err
|
||||
}
|
||||
|
||||
/// Retrieve the last error to occur as a (detailed) IoError.
|
||||
|
@ -36,11 +36,11 @@ use kinds::Send;
|
||||
use libc;
|
||||
use option::{Option, Some, None};
|
||||
use boxed::Box;
|
||||
use sys::fs::FileDesc;
|
||||
use result::{Ok, Err};
|
||||
use rt;
|
||||
use rt::local::Local;
|
||||
use rt::task::Task;
|
||||
use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
|
||||
use slice::SlicePrelude;
|
||||
use str::StrPrelude;
|
||||
use uint;
|
||||
@ -75,14 +75,14 @@ use uint;
|
||||
// case pipe also doesn't work, but magically file does!
|
||||
enum StdSource {
|
||||
TTY(Box<RtioTTY + Send>),
|
||||
File(Box<RtioFileStream + Send>),
|
||||
File(FileDesc),
|
||||
}
|
||||
|
||||
fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
|
||||
LocalIo::maybe_raise(|io| {
|
||||
Ok(match io.tty_open(fd, readable) {
|
||||
Ok(tty) => f(TTY(tty)),
|
||||
Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
|
||||
Err(_) => f(File(FileDesc::new(fd, false))),
|
||||
})
|
||||
}).map_err(IoError::from_rtio_error).unwrap()
|
||||
}
|
||||
@ -278,10 +278,10 @@ impl Reader for StdReader {
|
||||
// print!'d prompt not being shown until after the user hits
|
||||
// enter.
|
||||
flush();
|
||||
tty.read(buf)
|
||||
tty.read(buf).map_err(IoError::from_rtio_error)
|
||||
},
|
||||
File(ref mut file) => file.read(buf).map(|i| i as uint),
|
||||
}.map_err(IoError::from_rtio_error);
|
||||
};
|
||||
match ret {
|
||||
// When reading a piped stdin, libuv will return 0-length reads when
|
||||
// stdin reaches EOF. For pretty much all other streams it will
|
||||
@ -372,9 +372,9 @@ impl Writer for StdWriter {
|
||||
let max_size = if cfg!(windows) {8192} else {uint::MAX};
|
||||
for chunk in buf.chunks(max_size) {
|
||||
try!(match self.inner {
|
||||
TTY(ref mut tty) => tty.write(chunk),
|
||||
TTY(ref mut tty) => tty.write(chunk).map_err(IoError::from_rtio_error),
|
||||
File(ref mut file) => file.write(chunk),
|
||||
}.map_err(IoError::from_rtio_error))
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
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
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
@ -12,42 +12,40 @@
|
||||
|
||||
use alloc::arc::Arc;
|
||||
use libc::{mod, c_int};
|
||||
use std::c_str::CString;
|
||||
use std::mem;
|
||||
use std::os::windows::fill_utf16_buf_and_decode;
|
||||
use std::ptr;
|
||||
use std::rt::rtio;
|
||||
use std::rt::rtio::{IoResult, IoError};
|
||||
use std::str;
|
||||
|
||||
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;
|
||||
|
||||
struct Inner {
|
||||
fd: fd_t,
|
||||
pub struct FileDesc {
|
||||
/// The underlying C file descriptor.
|
||||
pub fd: fd_t,
|
||||
|
||||
/// Whether to close the file descriptor on drop.
|
||||
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
|
||||
}) }
|
||||
FileDesc { fd: fd, close_on_drop: close_on_drop }
|
||||
}
|
||||
|
||||
pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
pub fn read(&self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
let mut read = 0;
|
||||
let ret = unsafe {
|
||||
libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
|
||||
@ -60,7 +58,8 @@ impl FileDesc {
|
||||
Err(super::last_error())
|
||||
}
|
||||
}
|
||||
pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> IoResult<()> {
|
||||
let mut cur = buf.as_ptr();
|
||||
let mut remaining = buf.len();
|
||||
while remaining > 0 {
|
||||
@ -80,7 +79,7 @@ impl FileDesc {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> fd_t { self.inner.fd }
|
||||
pub fn fd(&self) -> fd_t { self.fd }
|
||||
|
||||
pub fn handle(&self) -> libc::HANDLE {
|
||||
unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
|
||||
@ -88,153 +87,67 @@ impl FileDesc {
|
||||
|
||||
// A version of seek that takes &self so that tell can call it
|
||||
// - the private seek should of course take &mut self.
|
||||
fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
|
||||
fn seek_common(&self, pos: i64, style: SeekStyle) -> IoResult<u64> {
|
||||
let whence = match style {
|
||||
rtio::SeekSet => libc::FILE_BEGIN,
|
||||
rtio::SeekEnd => libc::FILE_END,
|
||||
rtio::SeekCur => libc::FILE_CURRENT,
|
||||
SeekSet => libc::FILE_BEGIN,
|
||||
SeekEnd => libc::FILE_END,
|
||||
SeekCur => libc::FILE_CURRENT,
|
||||
};
|
||||
unsafe {
|
||||
let mut newpos = 0;
|
||||
match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
|
||||
whence) {
|
||||
match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, whence) {
|
||||
0 => Err(super::last_error()),
|
||||
_ => Ok(newpos as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl rtio::RtioFileStream for FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
|
||||
self.inner_read(buf).map(|i| i as int)
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
|
||||
fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
|
||||
let mut read = 0;
|
||||
let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
|
||||
overlap.Offset = offset as libc::DWORD;
|
||||
overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
|
||||
let ret = unsafe {
|
||||
libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
|
||||
buf.len() as libc::DWORD, &mut read,
|
||||
&mut overlap)
|
||||
};
|
||||
if ret != 0 {
|
||||
Ok(read as int)
|
||||
} else {
|
||||
Err(super::last_error())
|
||||
}
|
||||
}
|
||||
fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> {
|
||||
let mut cur = buf.as_ptr();
|
||||
let mut remaining = buf.len();
|
||||
let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
|
||||
while remaining > 0 {
|
||||
overlap.Offset = offset as libc::DWORD;
|
||||
overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
|
||||
let mut amt = 0;
|
||||
let ret = unsafe {
|
||||
libc::WriteFile(self.handle(), cur as libc::LPVOID,
|
||||
remaining as libc::DWORD, &mut amt,
|
||||
&mut overlap)
|
||||
};
|
||||
if ret != 0 {
|
||||
remaining -= amt as uint;
|
||||
cur = unsafe { cur.offset(amt as int) };
|
||||
offset += amt as u64;
|
||||
} else {
|
||||
return Err(super::last_error())
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
|
||||
pub fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<u64> {
|
||||
self.seek_common(pos, style)
|
||||
}
|
||||
|
||||
fn tell(&self) -> IoResult<u64> {
|
||||
self.seek_common(0, rtio::SeekCur)
|
||||
pub fn tell(&self) -> IoResult<u64> {
|
||||
self.seek_common(0, SeekCur)
|
||||
}
|
||||
|
||||
fn fsync(&mut self) -> IoResult<()> {
|
||||
pub fn fsync(&mut self) -> IoResult<()> {
|
||||
super::mkerr_winbool(unsafe {
|
||||
libc::FlushFileBuffers(self.handle())
|
||||
})
|
||||
}
|
||||
|
||||
fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
|
||||
pub fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
|
||||
|
||||
fn truncate(&mut self, offset: i64) -> IoResult<()> {
|
||||
pub fn truncate(&mut self, offset: i64) -> IoResult<()> {
|
||||
let orig_pos = try!(self.tell());
|
||||
let _ = try!(self.seek(offset, rtio::SeekSet));
|
||||
let _ = try!(self.seek(offset, SeekSet));
|
||||
let ret = unsafe {
|
||||
match libc::SetEndOfFile(self.handle()) {
|
||||
0 => Err(super::last_error()),
|
||||
_ => Ok(())
|
||||
}
|
||||
};
|
||||
let _ = self.seek(orig_pos as i64, rtio::SeekSet);
|
||||
let _ = self.seek(orig_pos as i64, SeekSet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn fstat(&mut self) -> IoResult<rtio::FileStat> {
|
||||
pub fn fstat(&mut self) -> IoResult<io::FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::fstat(self.fd(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
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 {
|
||||
impl Drop for FileDesc {
|
||||
fn drop(&mut self) {
|
||||
// closing stdio file handles makes no sense, so never do it. Also, note
|
||||
// that errors are ignored when closing a file descriptor. The reason
|
||||
@ -251,39 +164,26 @@ impl Drop for Inner {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_utf16(s: &CString) -> IoResult<Vec<u16>> {
|
||||
match s.as_str() {
|
||||
Some(s) => Ok({
|
||||
let mut s = s.utf16_units().collect::<Vec<u16>>();
|
||||
s.push(0);
|
||||
s
|
||||
}),
|
||||
None => Err(IoError {
|
||||
code: libc::ERROR_INVALID_NAME as uint,
|
||||
extra: 0,
|
||||
detail: Some("valid unicode input required".to_string()),
|
||||
})
|
||||
}
|
||||
pub fn to_utf16(s: &Path) -> IoResult<Vec<u16>> {
|
||||
sys::to_utf16(s.as_str())
|
||||
}
|
||||
|
||||
pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
|
||||
-> IoResult<FileDesc> {
|
||||
pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
|
||||
// Flags passed to open_osfhandle
|
||||
let flags = match fm {
|
||||
rtio::Open => 0,
|
||||
rtio::Append => libc::O_APPEND,
|
||||
rtio::Truncate => libc::O_TRUNC,
|
||||
Open => 0,
|
||||
Append => libc::O_APPEND,
|
||||
Truncate => libc::O_TRUNC,
|
||||
};
|
||||
let flags = match fa {
|
||||
rtio::Read => flags | libc::O_RDONLY,
|
||||
rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT,
|
||||
rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
|
||||
Read => flags | libc::O_RDONLY,
|
||||
Write => flags | libc::O_WRONLY | libc::O_CREAT,
|
||||
ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
|
||||
};
|
||||
|
||||
let mut dwDesiredAccess = match fa {
|
||||
rtio::Read => libc::FILE_GENERIC_READ,
|
||||
rtio::Write => libc::FILE_GENERIC_WRITE,
|
||||
rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
|
||||
Read => libc::FILE_GENERIC_READ,
|
||||
Write => libc::FILE_GENERIC_WRITE,
|
||||
ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
|
||||
};
|
||||
|
||||
// libuv has a good comment about this, but the basic idea is what we try to
|
||||
@ -293,15 +193,15 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
|
||||
libc::FILE_SHARE_DELETE;
|
||||
|
||||
let dwCreationDisposition = match (fm, fa) {
|
||||
(rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING,
|
||||
(rtio::Truncate, _) => libc::CREATE_ALWAYS,
|
||||
(rtio::Open, rtio::Read) => libc::OPEN_EXISTING,
|
||||
(rtio::Open, _) => libc::OPEN_ALWAYS,
|
||||
(rtio::Append, rtio::Read) => {
|
||||
(Truncate, Read) => libc::TRUNCATE_EXISTING,
|
||||
(Truncate, _) => libc::CREATE_ALWAYS,
|
||||
(Open, Read) => libc::OPEN_EXISTING,
|
||||
(Open, _) => libc::OPEN_ALWAYS,
|
||||
(Append, Read) => {
|
||||
dwDesiredAccess |= libc::FILE_APPEND_DATA;
|
||||
libc::OPEN_EXISTING
|
||||
}
|
||||
(rtio::Append, _) => {
|
||||
(Append, _) => {
|
||||
dwDesiredAccess &= !libc::FILE_WRITE_DATA;
|
||||
dwDesiredAccess |= libc::FILE_APPEND_DATA;
|
||||
libc::OPEN_ALWAYS
|
||||
@ -337,7 +237,7 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
|
||||
pub fn mkdir(p: &Path, _mode: uint) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_winbool(unsafe {
|
||||
// FIXME: turn mode into something useful? #2623
|
||||
@ -345,20 +245,15 @@ pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
|
||||
fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
|
||||
let root = unsafe { CString::new(root.as_ptr(), false) };
|
||||
let root = Path::new(root);
|
||||
|
||||
pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
|
||||
fn prune(root: &Path, dirs: Vec<Path>) -> Vec<Path> {
|
||||
dirs.into_iter().filter(|path| {
|
||||
path.as_vec() != b"." && path.as_vec() != b".."
|
||||
}).map(|path| root.join(path).to_c_str()).collect()
|
||||
}).map(|path| root.join(path)).collect()
|
||||
}
|
||||
|
||||
let star = Path::new(unsafe {
|
||||
CString::new(p.as_ptr(), false)
|
||||
}).join("*");
|
||||
let path = try!(to_utf16(&star.to_c_str()));
|
||||
let star = p.join("*");
|
||||
let path = try!(to_utf16(&star));
|
||||
|
||||
unsafe {
|
||||
let mut wfd = mem::zeroed();
|
||||
@ -374,8 +269,8 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
|
||||
None => {
|
||||
assert!(libc::FindClose(find_handle) != 0);
|
||||
return Err(IoError {
|
||||
code: super::c::ERROR_ILLEGAL_CHARACTER as uint,
|
||||
extra: 0,
|
||||
kind: io::InvalidInput,
|
||||
desc: "path was not valid UTF-16",
|
||||
detail: Some(format!("path was not valid UTF-16: {}", filename)),
|
||||
})
|
||||
}, // FIXME #12056: Convert the UCS-2 to invalid utf-8 instead of erroring
|
||||
@ -391,42 +286,74 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlink(p: &CString) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_winbool(unsafe {
|
||||
libc::DeleteFileW(p.as_ptr())
|
||||
})
|
||||
pub fn unlink(p: &Path) -> IoResult<()> {
|
||||
fn do_unlink(p_utf16: &Vec<u16>) -> IoResult<()> {
|
||||
super::mkerr_winbool(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })
|
||||
}
|
||||
|
||||
let p_utf16 = try!(to_utf16(p));
|
||||
let res = do_unlink(&p_utf16);
|
||||
match res {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
// FIXME: change the code below to use more direct calls
|
||||
// than `stat` and `chmod`, to avoid re-conversion to
|
||||
// utf16 etc.
|
||||
|
||||
// On unix, a readonly file can be successfully removed. On windows,
|
||||
// however, it cannot. To keep the two platforms in line with
|
||||
// respect to their behavior, catch this case on windows, attempt to
|
||||
// change it to read-write, and then remove the file.
|
||||
if e.kind == io::PermissionDenied {
|
||||
let stat = match stat(p) {
|
||||
Ok(stat) => stat,
|
||||
Err(..) => return Err(e),
|
||||
};
|
||||
if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
|
||||
|
||||
match chmod(p, (stat.perm | io::USER_WRITE).bits() as uint) {
|
||||
Ok(()) => do_unlink(&p_utf16),
|
||||
Err(..) => {
|
||||
// Try to put it back as we found it
|
||||
let _ = chmod(p, stat.perm.bits() as uint);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
|
||||
pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
|
||||
let old = try!(to_utf16(old));
|
||||
let new = try!(to_utf16(new));
|
||||
super::mkerr_winbool(unsafe {
|
||||
libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
|
||||
libc::MOVEFILE_REPLACE_EXISTING)
|
||||
libc::MoveFileExW(old.as_ptr(), new.as_ptr(), libc::MOVEFILE_REPLACE_EXISTING)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
|
||||
pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_libc(unsafe {
|
||||
mkerr_libc(unsafe {
|
||||
libc::wchmod(p.as_ptr(), mode as libc::c_int)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rmdir(p: &CString) -> IoResult<()> {
|
||||
pub fn rmdir(p: &Path) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
|
||||
mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
|
||||
pub fn chown(_p: &Path, _uid: int, _gid: int) -> IoResult<()> {
|
||||
// libuv has this as a no-op, so seems like this should as well?
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn readlink(p: &CString) -> IoResult<CString> {
|
||||
pub fn readlink(p: &Path) -> IoResult<Path> {
|
||||
// FIXME: I have a feeling that this reads intermediate symlinks as well.
|
||||
use io::c::compat::kernel32::GetFinalPathNameByHandleW;
|
||||
use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
|
||||
let p = try!(to_utf16(p));
|
||||
let handle = unsafe {
|
||||
libc::CreateFileW(p.as_ptr(),
|
||||
@ -449,18 +376,18 @@ pub fn readlink(p: &CString) -> IoResult<CString> {
|
||||
libc::VOLUME_NAME_DOS)
|
||||
});
|
||||
let ret = match ret {
|
||||
Some(ref s) if s.as_slice().starts_with(r"\\?\") => {
|
||||
Ok(Path::new(s.as_slice().slice_from(4)).to_c_str())
|
||||
Some(ref s) if s.as_slice().starts_with(r"\\?\") => { // "
|
||||
Ok(Path::new(s.as_slice().slice_from(4)))
|
||||
}
|
||||
Some(s) => Ok(Path::new(s).to_c_str()),
|
||||
Some(s) => Ok(Path::new(s)),
|
||||
None => Err(super::last_error()),
|
||||
};
|
||||
assert!(unsafe { libc::CloseHandle(handle) } != 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
use io::c::compat::kernel32::CreateSymbolicLinkW;
|
||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
use sys::c::compat::kernel32::CreateSymbolicLinkW;
|
||||
let src = try!(to_utf16(src));
|
||||
let dst = try!(to_utf16(dst));
|
||||
super::mkerr_winbool(unsafe {
|
||||
@ -468,7 +395,7 @@ pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let src = try!(to_utf16(src));
|
||||
let dst = try!(to_utf16(dst));
|
||||
super::mkerr_winbool(unsafe {
|
||||
@ -476,28 +403,37 @@ pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
})
|
||||
}
|
||||
|
||||
fn mkstat(stat: &libc::stat) -> rtio::FileStat {
|
||||
rtio::FileStat {
|
||||
fn mkstat(stat: &libc::stat) -> FileStat {
|
||||
FileStat {
|
||||
size: stat.st_size as u64,
|
||||
kind: stat.st_mode as u64,
|
||||
perm: stat.st_mode as u64,
|
||||
kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT {
|
||||
libc::S_IFREG => io::TypeFile,
|
||||
libc::S_IFDIR => io::TypeDirectory,
|
||||
libc::S_IFIFO => io::TypeNamedPipe,
|
||||
libc::S_IFBLK => io::TypeBlockSpecial,
|
||||
libc::S_IFLNK => io::TypeSymlink,
|
||||
_ => io::TypeUnknown,
|
||||
},
|
||||
perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
|
||||
created: stat.st_ctime as u64,
|
||||
modified: stat.st_mtime as u64,
|
||||
accessed: stat.st_atime as u64,
|
||||
device: stat.st_dev as u64,
|
||||
inode: stat.st_ino as u64,
|
||||
rdev: stat.st_rdev as u64,
|
||||
nlink: stat.st_nlink as u64,
|
||||
uid: stat.st_uid as u64,
|
||||
gid: stat.st_gid as u64,
|
||||
blksize: 0,
|
||||
blocks: 0,
|
||||
flags: 0,
|
||||
gen: 0,
|
||||
unstable: UnstableFileStat {
|
||||
device: stat.st_dev as u64,
|
||||
inode: stat.st_ino as u64,
|
||||
rdev: stat.st_rdev as u64,
|
||||
nlink: stat.st_nlink as u64,
|
||||
uid: stat.st_uid as u64,
|
||||
gid: stat.st_gid as u64,
|
||||
blksize:0,
|
||||
blocks: 0,
|
||||
flags: 0,
|
||||
gen: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
|
||||
pub fn stat(p: &Path) -> IoResult<FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
let p = try!(to_utf16(p));
|
||||
match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
|
||||
@ -506,18 +442,19 @@ pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lstat(_p: &CString) -> IoResult<rtio::FileStat> {
|
||||
// FIXME: move this to platform-specific modules (for now)?
|
||||
pub fn lstat(_p: &Path) -> IoResult<FileStat> {
|
||||
// FIXME: implementation is missing
|
||||
Err(super::unimpl())
|
||||
}
|
||||
|
||||
pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
let mut buf = libc::utimbuf {
|
||||
actime: atime as libc::time64_t,
|
||||
modtime: mtime as libc::time64_t,
|
||||
};
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_libc(unsafe {
|
||||
mkerr_libc(unsafe {
|
||||
libc::wutime(p.as_ptr(), &mut buf)
|
||||
})
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user