1
Fork 0

native: Improve windows file handling

This commit splits the file implementation into file_unix and file_win32. The
two implementations have diverged to the point that they share almost 0 code at
this point, so it's easier to maintain as separate files.

The other major change accompanied with this commit is that file::open is no
longer based on libc's open function on windows, but rather windows's CreateFile
function. This fixes dealing with binary files on windows (test added in
previous commit).

This also changes the read/write functions to use ReadFile and WriteFile instead
of libc's read/write.

Closes #12406
This commit is contained in:
Alex Crichton 2014-02-26 12:57:00 -08:00
parent 843c5e6308
commit cd9010c77e
11 changed files with 1146 additions and 1018 deletions

View file

@ -1,995 +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
#[allow(non_camel_case_types)];
use std::sync::arc::UnsafeArc;
use std::c_str::CString;
use std::io::IoError;
use std::io;
use std::libc::{c_int, c_void};
use std::libc;
use std::mem;
use std::os;
use std::rt::rtio;
use std::vec;
use io::{IoResult, retry};
#[cfg(windows)] use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
#[cfg(windows)] use std::ptr;
#[cfg(windows)] use std::str;
pub fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 {
#[cfg(windows)] static eintr: int = 0; // doesn't matter
#[cfg(not(windows))] static eintr: int = libc::EINTR as int;
let origamt = data.len();
let mut data = data.as_ptr();
let mut amt = origamt;
while amt > 0 {
let mut ret;
loop {
ret = f(data, amt);
if cfg!(windows) { break } // windows has no eintr
// if we get an eintr, then try again
if ret != -1 || os::errno() as int != eintr { break }
}
if ret == 0 {
break
} else if ret != -1 {
amt -= ret as uint;
data = unsafe { data.offset(ret as int) };
} else {
return ret;
}
}
return (origamt - amt) as i64;
}
pub type fd_t = libc::c_int;
struct Inner {
fd: fd_t,
close_on_drop: bool,
}
pub struct FileDesc {
priv inner: UnsafeArc<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: UnsafeArc::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]) -> Result<uint, IoError> {
#[cfg(windows)] type rlen = libc::c_uint;
#[cfg(not(windows))] type rlen = libc::size_t;
let ret = retry(|| unsafe {
libc::read(self.fd(),
buf.as_mut_ptr() as *mut libc::c_void,
buf.len() as rlen) as libc::c_int
});
if ret == 0 {
Err(io::standard_error(io::EndOfFile))
} else if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as uint)
}
}
pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
#[cfg(windows)] type wlen = libc::c_uint;
#[cfg(not(windows))] type wlen = libc::size_t;
let ret = keep_going(buf, |buf, len| {
unsafe {
libc::write(self.fd(), buf as *libc::c_void, len as wlen) as i64
}
});
if ret < 0 {
Err(super::last_error())
} else {
Ok(())
}
}
pub fn fd(&self) -> fd_t {
// This unsafety is fine because we're just reading off the file
// descriptor, no one is modifying this.
unsafe { (*self.inner.get()).fd }
}
}
impl io::Reader for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> io::IoResult<uint> {
self.inner_read(buf)
}
}
impl io::Writer for FileDesc {
fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
self.inner_write(buf)
}
}
impl rtio::RtioFileStream for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
self.inner_read(buf).map(|i| i as int)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
return os_pread(self.fd(), buf.as_ptr(), buf.len(), offset);
#[cfg(windows)]
fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<int> {
unsafe {
let mut overlap: libc::OVERLAPPED = mem::init();
let handle = libc::get_osfhandle(fd) as libc::HANDLE;
let mut bytes_read = 0;
overlap.Offset = offset as libc::DWORD;
overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
match libc::ReadFile(handle, buf as libc::LPVOID,
amt as libc::DWORD,
&mut bytes_read, &mut overlap) {
0 => Err(super::last_error()),
_ => Ok(bytes_read as int)
}
}
}
#[cfg(unix)]
fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<int> {
match retry(|| unsafe {
libc::pread(fd, buf as *libc::c_void, amt as libc::size_t,
offset as libc::off_t) as libc::c_int
}) {
-1 => Err(super::last_error()),
n => Ok(n as int)
}
}
}
fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
return os_pwrite(self.fd(), buf.as_ptr(), buf.len(), offset);
#[cfg(windows)]
fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> {
unsafe {
let mut overlap: libc::OVERLAPPED = mem::init();
let handle = libc::get_osfhandle(fd) as libc::HANDLE;
overlap.Offset = offset as libc::DWORD;
overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
match libc::WriteFile(handle, buf as libc::LPVOID,
amt as libc::DWORD,
ptr::mut_null(), &mut overlap) {
0 => Err(super::last_error()),
_ => Ok(()),
}
}
}
#[cfg(unix)]
fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::pwrite(fd, buf as *libc::c_void, amt as libc::size_t,
offset as libc::off_t)
} as c_int))
}
}
#[cfg(windows)]
fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> {
let whence = match style {
io::SeekSet => libc::FILE_BEGIN,
io::SeekEnd => libc::FILE_END,
io::SeekCur => libc::FILE_CURRENT,
};
unsafe {
let handle = libc::get_osfhandle(self.fd()) as libc::HANDLE;
let mut newpos = 0;
match libc::SetFilePointerEx(handle, pos, &mut newpos, whence) {
0 => Err(super::last_error()),
_ => Ok(newpos as u64),
}
}
}
#[cfg(unix)]
fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result<u64, IoError> {
let whence = match whence {
io::SeekSet => libc::SEEK_SET,
io::SeekEnd => libc::SEEK_END,
io::SeekCur => libc::SEEK_CUR,
};
let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
if n < 0 {
Err(super::last_error())
} else {
Ok(n as u64)
}
}
fn tell(&self) -> Result<u64, IoError> {
let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
if n < 0 {
Err(super::last_error())
} else {
Ok(n as u64)
}
}
fn fsync(&mut self) -> Result<(), IoError> {
return os_fsync(self.fd());
#[cfg(windows)]
fn os_fsync(fd: c_int) -> IoResult<()> {
super::mkerr_winbool(unsafe {
let handle = libc::get_osfhandle(fd);
libc::FlushFileBuffers(handle as libc::HANDLE)
})
}
#[cfg(unix)]
fn os_fsync(fd: c_int) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe { libc::fsync(fd) }))
}
}
#[cfg(windows)]
fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); }
#[cfg(not(windows))]
fn datasync(&mut self) -> Result<(), IoError> {
return super::mkerr_libc(os_datasync(self.fd()));
#[cfg(target_os = "macos")]
fn os_datasync(fd: c_int) -> c_int {
unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
}
#[cfg(target_os = "linux")]
fn os_datasync(fd: c_int) -> c_int {
retry(|| unsafe { libc::fdatasync(fd) })
}
#[cfg(not(target_os = "macos"), not(target_os = "linux"))]
fn os_datasync(fd: c_int) -> c_int {
retry(|| unsafe { libc::fsync(fd) })
}
}
#[cfg(windows)]
fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
let orig_pos = match self.tell() { Ok(i) => i, Err(e) => return Err(e) };
match self.seek(offset, io::SeekSet) {
Ok(_) => {}, Err(e) => return Err(e),
};
let ret = unsafe {
let handle = libc::get_osfhandle(self.fd()) as libc::HANDLE;
match libc::SetEndOfFile(handle) {
0 => Err(super::last_error()),
_ => Ok(())
}
};
let _ = self.seek(orig_pos as i64, io::SeekSet);
return ret;
}
#[cfg(unix)]
fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
super::mkerr_libc(retry(|| unsafe {
libc::ftruncate(self.fd(), offset as libc::off_t)
}))
}
}
impl rtio::RtioPipe for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
self.inner_read(buf)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn clone(&self) -> ~rtio::RtioPipe {
~FileDesc { inner: self.inner.clone() } as ~rtio::RtioPipe
}
}
impl rtio::RtioTTY for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
self.inner_read(buf)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> {
Err(super::unimpl())
}
fn get_winsize(&mut self) -> Result<(int, int), IoError> {
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 {
warn!("error {} when closing file descriptor {}", n, self.fd);
}
}
}
}
pub struct CFile {
priv file: *libc::FILE,
priv 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: *libc::FILE) -> CFile {
CFile {
file: file,
fd: FileDesc::new(unsafe { libc::fileno(file) }, false)
}
}
pub fn flush(&mut self) -> Result<(), IoError> {
super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) }))
}
}
impl rtio::RtioFileStream for CFile {
fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
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(io::standard_error(io::EndOfFile))
} else if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as int)
}
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
let ret = keep_going(buf, |buf, len| {
unsafe {
libc::fwrite(buf as *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) -> Result<int, IoError> {
self.flush().and_then(|()| self.fd.pread(buf, offset))
}
fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.pwrite(buf, offset))
}
fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> {
let whence = match style {
io::SeekSet => libc::SEEK_SET,
io::SeekEnd => libc::SEEK_END,
io::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) -> Result<u64, IoError> {
let ret = unsafe { libc::ftell(self.file) };
if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as u64)
}
}
fn fsync(&mut self) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.fsync())
}
fn datasync(&mut self) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.fsync())
}
fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.truncate(offset))
}
}
impl Drop for CFile {
fn drop(&mut self) {
unsafe { let _ = libc::fclose(self.file); }
}
}
pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
-> IoResult<FileDesc> {
let flags = match fm {
io::Open => 0,
io::Append => libc::O_APPEND,
io::Truncate => libc::O_TRUNC,
};
// Opening with a write permission must silently create the file.
let (flags, mode) = match fa {
io::Read => (flags | libc::O_RDONLY, 0),
io::Write => (flags | libc::O_WRONLY | libc::O_CREAT,
libc::S_IRUSR | libc::S_IWUSR),
io::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
libc::S_IRUSR | libc::S_IWUSR),
};
return match os_open(path, flags, mode) {
-1 => Err(super::last_error()),
fd => Ok(FileDesc::new(fd, true)),
};
#[cfg(windows)]
fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int {
as_utf16_p(path.as_str().unwrap(), |path| {
retry(|| unsafe { libc::wopen(path, flags, mode) })
})
}
#[cfg(unix)]
fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int {
retry(|| unsafe { libc::open(path.with_ref(|p| p), flags, mode) })
}
}
pub fn mkdir(p: &CString, mode: io::FilePermission) -> IoResult<()> {
return os_mkdir(p, mode as c_int);
#[cfg(windows)]
fn os_mkdir(p: &CString, _mode: c_int) -> IoResult<()> {
super::mkerr_winbool(unsafe {
// FIXME: turn mode into something useful? #2623
as_utf16_p(p.as_str().unwrap(), |buf| {
libc::CreateDirectoryW(buf, ptr::mut_null())
})
})
}
#[cfg(unix)]
fn os_mkdir(p: &CString, mode: c_int) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::mkdir(p.with_ref(|p| p), mode as libc::mode_t)
}))
}
}
pub fn readdir(p: &CString) -> IoResult<~[Path]> {
fn prune(root: &CString, dirs: ~[Path]) -> ~[Path] {
let root = unsafe { CString::new(root.with_ref(|p| p), false) };
let root = Path::new(root);
dirs.move_iter().filter(|path| {
path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
}).map(|path| root.join(path)).collect()
}
unsafe {
#[cfg(not(windows))]
unsafe fn get_list(p: &CString) -> IoResult<~[Path]> {
use std::libc::{dirent_t};
use std::libc::{opendir, readdir, closedir};
extern {
fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char;
}
debug!("os::list_dir -- BEFORE OPENDIR");
let dir_ptr = p.with_ref(|buf| opendir(buf));
if dir_ptr as uint != 0 {
let mut paths = ~[];
debug!("os::list_dir -- opendir() SUCCESS");
let mut entry_ptr = readdir(dir_ptr);
while entry_ptr as uint != 0 {
let cstr = CString::new(rust_list_dir_val(entry_ptr), false);
paths.push(Path::new(cstr));
entry_ptr = readdir(dir_ptr);
}
assert_eq!(closedir(dir_ptr), 0);
Ok(paths)
} else {
Err(super::last_error())
}
}
#[cfg(windows)]
unsafe fn get_list(p: &CString) -> IoResult<~[Path]> {
use std::libc::consts::os::extra::INVALID_HANDLE_VALUE;
use std::libc::{wcslen, free};
use std::libc::funcs::extra::kernel32::{
FindFirstFileW,
FindNextFileW,
FindClose,
};
use std::libc::types::os::arch::extra::HANDLE;
use os::win32::{
as_utf16_p
};
use rt::global_heap::malloc_raw;
#[nolink]
extern {
fn rust_list_dir_wfd_size() -> libc::size_t;
fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
}
let p = CString::new(p.with_ref(|p| p), false);
let p = Path::new(p);
let star = p.join("*");
as_utf16_p(star.as_str().unwrap(), |path_ptr| {
let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE);
if find_handle as libc::c_int != INVALID_HANDLE_VALUE {
let mut paths = ~[];
let mut more_files = 1 as libc::c_int;
while more_files != 0 {
let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr as *c_void);
if fp_buf as uint == 0 {
fail!("os::list_dir() failure: got null ptr from wfd");
}
else {
let fp_vec = vec::from_buf(
fp_buf, wcslen(fp_buf) as uint);
let fp_trimmed = str::truncate_utf16_at_nul(fp_vec);
let fp_str = str::from_utf16(fp_trimmed)
.expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16");
paths.push(Path::new(fp_str));
}
more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE);
}
assert!(FindClose(find_handle) != 0);
free(wfd_ptr as *mut c_void);
Ok(paths)
} else {
Err(super::last_error())
}
})
}
get_list(p).map(|paths| prune(p, paths))
}
}
pub fn unlink(p: &CString) -> IoResult<()> {
return os_unlink(p);
#[cfg(windows)]
fn os_unlink(p: &CString) -> IoResult<()> {
super::mkerr_winbool(unsafe {
as_utf16_p(p.as_str().unwrap(), |buf| {
libc::DeleteFileW(buf)
})
})
}
#[cfg(unix)]
fn os_unlink(p: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe { libc::unlink(p.with_ref(|p| p)) }))
}
}
pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
return os_rename(old, new);
#[cfg(windows)]
fn os_rename(old: &CString, new: &CString) -> IoResult<()> {
super::mkerr_winbool(unsafe {
as_utf16_p(old.as_str().unwrap(), |old| {
as_utf16_p(new.as_str().unwrap(), |new| {
libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING)
})
})
})
}
#[cfg(unix)]
fn os_rename(old: &CString, new: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::rename(old.with_ref(|p| p), new.with_ref(|p| p))
}))
}
}
pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> {
return super::mkerr_libc(os_chmod(p, mode as c_int));
#[cfg(windows)]
fn os_chmod(p: &CString, mode: c_int) -> c_int {
unsafe {
as_utf16_p(p.as_str().unwrap(), |p| retry(|| {
libc::wchmod(p, mode)
}))
}
}
#[cfg(unix)]
fn os_chmod(p: &CString, mode: c_int) -> c_int {
retry(||unsafe { libc::chmod(p.with_ref(|p| p), mode as libc::mode_t) })
}
}
pub fn rmdir(p: &CString) -> IoResult<()> {
return super::mkerr_libc(os_rmdir(p));
#[cfg(windows)]
fn os_rmdir(p: &CString) -> c_int {
unsafe {
as_utf16_p(p.as_str().unwrap(), |p| retry(|| {
libc::wrmdir(p)
}))
}
}
#[cfg(unix)]
fn os_rmdir(p: &CString) -> c_int {
retry(|| unsafe { libc::rmdir(p.with_ref(|p| p)) })
}
}
pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> {
return super::mkerr_libc(os_chown(p, uid, gid));
// libuv has this as a no-op, so seems like this should as well?
#[cfg(windows)]
fn os_chown(_p: &CString, _uid: int, _gid: int) -> c_int { 0 }
#[cfg(unix)]
fn os_chown(p: &CString, uid: int, gid: int) -> c_int {
retry(|| unsafe {
libc::chown(p.with_ref(|p| p), uid as libc::uid_t,
gid as libc::gid_t)
})
}
}
pub fn readlink(p: &CString) -> IoResult<Path> {
return os_readlink(p);
// FIXME: I have a feeling that this reads intermediate symlinks as well.
#[cfg(windows)]
fn os_readlink(p: &CString) -> IoResult<Path> {
let handle = unsafe {
as_utf16_p(p.as_str().unwrap(), |p| {
libc::CreateFileW(p,
libc::GENERIC_READ,
libc::FILE_SHARE_READ,
ptr::mut_null(),
libc::OPEN_EXISTING,
libc::FILE_ATTRIBUTE_NORMAL,
ptr::mut_null())
})
};
if handle as int == libc::INVALID_HANDLE_VALUE as int {
return Err(super::last_error())
}
let ret = fill_utf16_buf_and_decode(|buf, sz| {
unsafe {
libc::GetFinalPathNameByHandleW(handle, buf as *u16, sz,
libc::VOLUME_NAME_NT)
}
});
let ret = match ret {
Some(s) => Ok(Path::new(s)),
None => Err(super::last_error()),
};
assert!(unsafe { libc::CloseHandle(handle) } != 0);
return ret;
}
#[cfg(unix)]
fn os_readlink(p: &CString) -> IoResult<Path> {
let p = p.with_ref(|p| p);
let mut len = unsafe { libc::pathconf(p, libc::_PC_NAME_MAX) };
if len == -1 {
len = 1024; // FIXME: read PATH_MAX from C ffi?
}
let mut buf = vec::with_capacity::<u8>(len as uint);
match retry(|| 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: &CString, dst: &CString) -> IoResult<()> {
return os_symlink(src, dst);
#[cfg(windows)]
fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
as_utf16_p(dst.as_str().unwrap(), |dst| {
unsafe { libc::CreateSymbolicLinkW(dst, src, 0) }
}) as libc::BOOL
}))
}
#[cfg(unix)]
fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::symlink(src.with_ref(|p| p), dst.with_ref(|p| p))
}))
}
}
pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
return os_link(src, dst);
#[cfg(windows)]
fn os_link(src: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
as_utf16_p(dst.as_str().unwrap(), |dst| {
unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) }
})
}))
}
#[cfg(unix)]
fn os_link(src: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::link(src.with_ref(|p| p), dst.with_ref(|p| p))
}))
}
}
#[cfg(windows)]
fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
let path = unsafe { CString::new(path.with_ref(|p| p), false) };
let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
libc::S_IFREG => io::TypeFile,
libc::S_IFDIR => io::TypeDirectory,
libc::S_IFIFO => io::TypeNamedPipe,
libc::S_IFBLK => io::TypeBlockSpecial,
libc::S_IFLNK => io::TypeSymlink,
_ => io::TypeUnknown,
};
io::FileStat {
path: Path::new(path),
size: stat.st_size as u64,
kind: kind,
perm: (stat.st_mode) as io::FilePermission & io::AllPermissions,
created: stat.st_ctime as u64,
modified: stat.st_mtime as u64,
accessed: stat.st_atime as u64,
unstable: io::UnstableFileStat {
device: stat.st_dev as u64,
inode: stat.st_ino as u64,
rdev: stat.st_rdev as u64,
nlink: stat.st_nlink as u64,
uid: stat.st_uid as u64,
gid: stat.st_gid as u64,
blksize: 0,
blocks: 0,
flags: 0,
gen: 0,
}
}
}
#[cfg(unix)]
fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
let path = unsafe { CString::new(path.with_ref(|p| p), false) };
// FileStat times are in milliseconds
fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
libc::S_IFREG => io::TypeFile,
libc::S_IFDIR => io::TypeDirectory,
libc::S_IFIFO => io::TypeNamedPipe,
libc::S_IFBLK => io::TypeBlockSpecial,
libc::S_IFLNK => io::TypeSymlink,
_ => io::TypeUnknown,
};
#[cfg(not(target_os = "linux"), not(target_os = "android"))]
fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
#[cfg(target_os = "linux")] #[cfg(target_os = "android")]
fn flags(_stat: &libc::stat) -> u64 { 0 }
#[cfg(not(target_os = "linux"), not(target_os = "android"))]
fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
#[cfg(target_os = "linux")] #[cfg(target_os = "android")]
fn gen(_stat: &libc::stat) -> u64 { 0 }
io::FileStat {
path: Path::new(path),
size: stat.st_size as u64,
kind: kind,
perm: (stat.st_mode) as io::FilePermission & io::AllPermissions,
created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
unstable: io::UnstableFileStat {
device: stat.st_dev as u64,
inode: stat.st_ino as u64,
rdev: stat.st_rdev as u64,
nlink: stat.st_nlink as u64,
uid: stat.st_uid as u64,
gid: stat.st_gid as u64,
blksize: stat.st_blksize as u64,
blocks: stat.st_blocks as u64,
flags: flags(stat),
gen: gen(stat),
}
}
}
pub fn stat(p: &CString) -> IoResult<io::FileStat> {
return os_stat(p);
#[cfg(windows)]
fn os_stat(p: &CString) -> IoResult<io::FileStat> {
let mut stat: libc::stat = unsafe { mem::uninit() };
as_utf16_p(p.as_str().unwrap(), |up| {
match retry(|| unsafe { libc::wstat(up, &mut stat) }) {
0 => Ok(mkstat(&stat, p)),
_ => Err(super::last_error()),
}
})
}
#[cfg(unix)]
fn os_stat(p: &CString) -> IoResult<io::FileStat> {
let mut stat: libc::stat = unsafe { mem::uninit() };
match retry(|| unsafe { libc::stat(p.with_ref(|p| p), &mut stat) }) {
0 => Ok(mkstat(&stat, p)),
_ => Err(super::last_error()),
}
}
}
pub fn lstat(p: &CString) -> IoResult<io::FileStat> {
return os_lstat(p);
// FIXME: windows implementation is missing
#[cfg(windows)]
fn os_lstat(_p: &CString) -> IoResult<io::FileStat> {
Err(super::unimpl())
}
#[cfg(unix)]
fn os_lstat(p: &CString) -> IoResult<io::FileStat> {
let mut stat: libc::stat = unsafe { mem::uninit() };
match retry(|| unsafe { libc::lstat(p.with_ref(|p| p), &mut stat) }) {
0 => Ok(mkstat(&stat, p)),
_ => Err(super::last_error()),
}
}
}
pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
return super::mkerr_libc(os_utime(p, atime, mtime));
#[cfg(windows)]
fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int {
let buf = libc::utimbuf {
actime: (atime / 1000) as libc::time64_t,
modtime: (mtime / 1000) as libc::time64_t,
};
unsafe {
as_utf16_p(p.as_str().unwrap(), |p| retry(|| {
libc::wutime(p, &buf)
}))
}
}
#[cfg(unix)]
fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int {
let buf = libc::utimbuf {
actime: (atime / 1000) as libc::time_t,
modtime: (mtime / 1000) as libc::time_t,
};
retry(|| unsafe { libc::utime(p.with_ref(|p| p), &buf) })
}
}
#[cfg(test)]
mod tests {
use super::{CFile, FileDesc};
use std::io;
use std::libc;
use std::os;
use std::rt::rtio::RtioFileStream;
#[ignore(cfg(target_os = "freebsd"))] // 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.
unsafe {
let os::Pipe { input, out } = os::pipe();
let mut reader = FileDesc::new(input, true);
let mut writer = FileDesc::new(out, true);
writer.inner_write(bytes!("test")).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 => fail!("invalid read: {:?}", r)
}
assert!(writer.inner_read(buf).is_err());
assert!(reader.inner_write(buf).is_err());
}
}
#[ignore(cfg(windows))] // apparently windows doesn't like tmpfile
#[test]
fn test_cfile() {
unsafe {
let f = libc::tmpfile();
assert!(!f.is_null());
let mut file = CFile::new(f);
file.write(bytes!("test")).unwrap();
let mut buf = [0u8, ..4];
let _ = file.seek(0, io::SeekSet).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 => fail!("invalid read: {:?}", r)
}
}
}
}

View file

@ -0,0 +1,573 @@
// 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 std::sync::arc::UnsafeArc;
use std::c_str::CString;
use std::io::IoError;
use std::io;
use std::libc::{c_int, c_void};
use std::libc;
use std::mem;
use std::rt::rtio;
use std::vec;
use io::{IoResult, retry, keep_going};
pub type fd_t = libc::c_int;
struct Inner {
fd: fd_t,
close_on_drop: bool,
}
pub struct FileDesc {
priv inner: UnsafeArc<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: UnsafeArc::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]) -> Result<uint, IoError> {
let ret = retry(|| unsafe {
libc::read(self.fd(),
buf.as_mut_ptr() as *mut libc::c_void,
buf.len() as libc::size_t) as libc::c_int
});
if ret == 0 {
Err(io::standard_error(io::EndOfFile))
} else if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as uint)
}
}
pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
let ret = keep_going(buf, |buf, len| {
unsafe {
libc::write(self.fd(), buf as *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 {
// This unsafety is fine because we're just reading off the file
// descriptor, no one is modifying this.
unsafe { (*self.inner.get()).fd }
}
}
impl io::Reader for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> io::IoResult<uint> {
self.inner_read(buf)
}
}
impl io::Writer for FileDesc {
fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
self.inner_write(buf)
}
}
impl rtio::RtioFileStream for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
self.inner_read(buf).map(|i| i as int)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
match retry(|| unsafe {
libc::pread(self.fd(), buf.as_ptr() as *libc::c_void,
buf.len() as libc::size_t,
offset as libc::off_t) as libc::c_int
}) {
-1 => Err(super::last_error()),
n => Ok(n as int)
}
}
fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
super::mkerr_libc(retry(|| unsafe {
libc::pwrite(self.fd(), buf.as_ptr() as *libc::c_void,
buf.len() as libc::size_t, offset as libc::off_t)
} as c_int))
}
fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result<u64, IoError> {
let whence = match whence {
io::SeekSet => libc::SEEK_SET,
io::SeekEnd => libc::SEEK_END,
io::SeekCur => libc::SEEK_CUR,
};
let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
if n < 0 {
Err(super::last_error())
} else {
Ok(n as u64)
}
}
fn tell(&self) -> Result<u64, IoError> {
let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
if n < 0 {
Err(super::last_error())
} else {
Ok(n as u64)
}
}
fn fsync(&mut self) -> Result<(), IoError> {
super::mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
}
fn datasync(&mut self) -> Result<(), IoError> {
return super::mkerr_libc(os_datasync(self.fd()));
#[cfg(target_os = "macos")]
fn os_datasync(fd: c_int) -> c_int {
unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
}
#[cfg(target_os = "linux")]
fn os_datasync(fd: c_int) -> c_int {
retry(|| unsafe { libc::fdatasync(fd) })
}
#[cfg(not(target_os = "macos"), not(target_os = "linux"))]
fn os_datasync(fd: c_int) -> c_int {
retry(|| unsafe { libc::fsync(fd) })
}
}
fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
super::mkerr_libc(retry(|| unsafe {
libc::ftruncate(self.fd(), offset as libc::off_t)
}))
}
}
impl rtio::RtioPipe for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
self.inner_read(buf)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn clone(&self) -> ~rtio::RtioPipe {
~FileDesc { inner: self.inner.clone() } as ~rtio::RtioPipe
}
}
impl rtio::RtioTTY for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
self.inner_read(buf)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> {
Err(super::unimpl())
}
fn get_winsize(&mut self) -> Result<(int, int), IoError> {
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 {
warn!("error {} when closing file descriptor {}", n, self.fd);
}
}
}
}
pub struct CFile {
priv file: *libc::FILE,
priv 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: *libc::FILE) -> CFile {
CFile {
file: file,
fd: FileDesc::new(unsafe { libc::fileno(file) }, false)
}
}
pub fn flush(&mut self) -> Result<(), IoError> {
super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) }))
}
}
impl rtio::RtioFileStream for CFile {
fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
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(io::standard_error(io::EndOfFile))
} else if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as int)
}
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
let ret = keep_going(buf, |buf, len| {
unsafe {
libc::fwrite(buf as *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) -> Result<int, IoError> {
self.flush().and_then(|()| self.fd.pread(buf, offset))
}
fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.pwrite(buf, offset))
}
fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> {
let whence = match style {
io::SeekSet => libc::SEEK_SET,
io::SeekEnd => libc::SEEK_END,
io::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) -> Result<u64, IoError> {
let ret = unsafe { libc::ftell(self.file) };
if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as u64)
}
}
fn fsync(&mut self) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.fsync())
}
fn datasync(&mut self) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.fsync())
}
fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
self.flush().and_then(|()| self.fd.truncate(offset))
}
}
impl Drop for CFile {
fn drop(&mut self) {
unsafe { let _ = libc::fclose(self.file); }
}
}
pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
-> IoResult<FileDesc> {
let flags = match fm {
io::Open => 0,
io::Append => libc::O_APPEND,
io::Truncate => libc::O_TRUNC,
};
// Opening with a write permission must silently create the file.
let (flags, mode) = match fa {
io::Read => (flags | libc::O_RDONLY, 0),
io::Write => (flags | libc::O_WRONLY | libc::O_CREAT,
libc::S_IRUSR | libc::S_IWUSR),
io::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
libc::S_IRUSR | libc::S_IWUSR),
};
match retry(|| unsafe { libc::open(path.with_ref(|p| p), flags, mode) }) {
-1 => Err(super::last_error()),
fd => Ok(FileDesc::new(fd, true)),
}
}
pub fn mkdir(p: &CString, mode: io::FilePermission) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::mkdir(p.with_ref(|p| p), mode as libc::mode_t)
}))
}
pub fn readdir(p: &CString) -> IoResult<~[Path]> {
use std::libc::{dirent_t};
use std::libc::{opendir, readdir, closedir};
fn prune(root: &CString, dirs: ~[Path]) -> ~[Path] {
let root = unsafe { CString::new(root.with_ref(|p| p), false) };
let root = Path::new(root);
dirs.move_iter().filter(|path| {
path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
}).map(|path| root.join(path)).collect()
}
extern {
fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char;
}
debug!("os::list_dir -- BEFORE OPENDIR");
let dir_ptr = p.with_ref(|buf| unsafe { opendir(buf) });
if dir_ptr as uint != 0 {
let mut paths = ~[];
debug!("os::list_dir -- opendir() SUCCESS");
let mut entry_ptr = unsafe { readdir(dir_ptr) };
while entry_ptr as uint != 0 {
let cstr = unsafe {
CString::new(rust_list_dir_val(entry_ptr), false)
};
paths.push(Path::new(cstr));
entry_ptr = unsafe { readdir(dir_ptr) };
}
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(retry(|| unsafe { libc::unlink(p.with_ref(|p| p)) }))
}
pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::rename(old.with_ref(|p| p), new.with_ref(|p| p))
}))
}
pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::chmod(p.with_ref(|p| p), mode as libc::mode_t)
}))
}
pub fn rmdir(p: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::rmdir(p.with_ref(|p| p))
}))
}
pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::chown(p.with_ref(|p| p), uid as libc::uid_t,
gid as libc::gid_t)
}))
}
pub fn readlink(p: &CString) -> IoResult<Path> {
let p = p.with_ref(|p| p);
let mut len = unsafe { libc::pathconf(p, libc::_PC_NAME_MAX) };
if len == -1 {
len = 1024; // FIXME: read PATH_MAX from C ffi?
}
let mut buf = vec::with_capacity::<u8>(len as uint);
match retry(|| 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: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::symlink(src.with_ref(|p| p), dst.with_ref(|p| p))
}))
}
pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_libc(retry(|| unsafe {
libc::link(src.with_ref(|p| p), dst.with_ref(|p| p))
}))
}
fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
let path = unsafe { CString::new(path.with_ref(|p| p), false) };
// FileStat times are in milliseconds
fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
libc::S_IFREG => io::TypeFile,
libc::S_IFDIR => io::TypeDirectory,
libc::S_IFIFO => io::TypeNamedPipe,
libc::S_IFBLK => io::TypeBlockSpecial,
libc::S_IFLNK => io::TypeSymlink,
_ => io::TypeUnknown,
};
#[cfg(not(target_os = "linux"), not(target_os = "android"))]
fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
#[cfg(target_os = "linux")] #[cfg(target_os = "android")]
fn flags(_stat: &libc::stat) -> u64 { 0 }
#[cfg(not(target_os = "linux"), not(target_os = "android"))]
fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
#[cfg(target_os = "linux")] #[cfg(target_os = "android")]
fn gen(_stat: &libc::stat) -> u64 { 0 }
io::FileStat {
path: Path::new(path),
size: stat.st_size as u64,
kind: kind,
perm: (stat.st_mode) as io::FilePermission & io::AllPermissions,
created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
unstable: io::UnstableFileStat {
device: stat.st_dev as u64,
inode: stat.st_ino as u64,
rdev: stat.st_rdev as u64,
nlink: stat.st_nlink as u64,
uid: stat.st_uid as u64,
gid: stat.st_gid as u64,
blksize: stat.st_blksize as u64,
blocks: stat.st_blocks as u64,
flags: flags(stat),
gen: gen(stat),
}
}
}
pub fn stat(p: &CString) -> IoResult<io::FileStat> {
let mut stat: libc::stat = unsafe { mem::uninit() };
match retry(|| unsafe { libc::stat(p.with_ref(|p| p), &mut stat) }) {
0 => Ok(mkstat(&stat, p)),
_ => Err(super::last_error()),
}
}
pub fn lstat(p: &CString) -> IoResult<io::FileStat> {
let mut stat: libc::stat = unsafe { mem::uninit() };
match retry(|| unsafe { libc::lstat(p.with_ref(|p| p), &mut stat) }) {
0 => Ok(mkstat(&stat, p)),
_ => Err(super::last_error()),
}
}
pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
let buf = libc::utimbuf {
actime: (atime / 1000) as libc::time_t,
modtime: (mtime / 1000) as libc::time_t,
};
super::mkerr_libc(retry(|| unsafe {
libc::utime(p.with_ref(|p| p), &buf)
}))
}
#[cfg(test)]
mod tests {
use super::{CFile, FileDesc};
use std::io;
use std::libc;
use std::os;
use std::rt::rtio::RtioFileStream;
#[ignore(cfg(target_os = "freebsd"))] // 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.
unsafe {
let os::Pipe { input, out } = os::pipe();
let mut reader = FileDesc::new(input, true);
let mut writer = FileDesc::new(out, true);
writer.inner_write(bytes!("test")).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 => fail!("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(bytes!("test")).unwrap();
let mut buf = [0u8, ..4];
let _ = file.seek(0, io::SeekSet).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 => fail!("invalid read: {:?}", r)
}
}
}
}

View file

@ -0,0 +1,516 @@
// 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 win32-based file I/O
use std::c_str::CString;
use std::cast;
use std::io::IoError;
use std::io;
use std::libc::{c_int, c_void};
use std::libc;
use std::mem;
use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
use std::ptr;
use std::rt::rtio;
use std::str;
use std::sync::arc::UnsafeArc;
use std::vec;
use io::IoResult;
pub type fd_t = libc::c_int;
struct Inner {
fd: fd_t,
close_on_drop: bool,
}
pub struct FileDesc {
priv inner: UnsafeArc<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: UnsafeArc::new(Inner {
fd: fd,
close_on_drop: close_on_drop
}) }
}
pub fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
let mut read = 0;
let ret = unsafe {
libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
buf.len() as libc::DWORD, &mut read,
ptr::mut_null())
};
if ret != 0 {
Ok(read as uint)
} else {
Err(super::last_error())
}
}
pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
let mut cur = buf.as_ptr();
let mut remaining = buf.len();
while remaining > 0 {
let mut amt = 0;
let ret = unsafe {
libc::WriteFile(self.handle(), cur as libc::LPVOID,
remaining as libc::DWORD, &mut amt,
ptr::mut_null())
};
if ret != 0 {
remaining -= amt as uint;
cur = unsafe { cur.offset(amt as int) };
} else {
return Err(super::last_error())
}
}
Ok(())
}
pub fn fd(&self) -> fd_t {
// This unsafety is fine because we're just reading off the file
// descriptor, no one is modifying this.
unsafe { (*self.inner.get()).fd }
}
pub fn handle(&self) -> libc::HANDLE {
unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
}
}
impl io::Reader for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> io::IoResult<uint> {
self.inner_read(buf)
}
}
impl io::Writer for FileDesc {
fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
self.inner_write(buf)
}
}
impl rtio::RtioFileStream for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
self.inner_read(buf).map(|i| i as int)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
let mut read = 0;
let mut overlap: libc::OVERLAPPED = unsafe { mem::init() };
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) -> Result<(), IoError> {
let mut cur = buf.as_ptr();
let mut remaining = buf.len();
let mut overlap: libc::OVERLAPPED = unsafe { mem::init() };
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: io::SeekStyle) -> Result<u64, IoError> {
let whence = match style {
io::SeekSet => libc::FILE_BEGIN,
io::SeekEnd => libc::FILE_END,
io::SeekCur => libc::FILE_CURRENT,
};
unsafe {
let mut newpos = 0;
match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
whence) {
0 => Err(super::last_error()),
_ => Ok(newpos as u64),
}
}
}
fn tell(&self) -> Result<u64, IoError> {
// This transmute is fine because our seek implementation doesn't
// actually use the mutable self at all.
unsafe { cast::transmute_mut(self).seek(0, io::SeekCur) }
}
fn fsync(&mut self) -> Result<(), IoError> {
super::mkerr_winbool(unsafe {
libc::FlushFileBuffers(self.handle())
})
}
fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); }
fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
let orig_pos = try!(self.tell());
let _ = try!(self.seek(offset, io::SeekSet));
let ret = unsafe {
match libc::SetEndOfFile(self.handle()) {
0 => Err(super::last_error()),
_ => Ok(())
}
};
let _ = self.seek(orig_pos as i64, io::SeekSet);
return ret;
}
}
impl rtio::RtioPipe for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
self.inner_read(buf)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn clone(&self) -> ~rtio::RtioPipe {
~FileDesc { inner: self.inner.clone() } as ~rtio::RtioPipe
}
}
impl rtio::RtioTTY for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
self.inner_read(buf)
}
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.inner_write(buf)
}
fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> {
Err(super::unimpl())
}
fn get_winsize(&mut self) -> Result<(int, int), IoError> {
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 {
warn!("error {} when closing file descriptor {}", n, self.fd);
}
}
}
}
pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
-> IoResult<FileDesc> {
// Flags passed to open_osfhandle
let flags = match fm {
io::Open => 0,
io::Append => libc::O_APPEND,
io::Truncate => libc::O_TRUNC,
};
let flags = match fa {
io::Read => flags | libc::O_RDONLY,
io::Write => flags | libc::O_WRONLY | libc::O_CREAT,
io::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
};
let mut dwDesiredAccess = match fa {
io::Read => libc::FILE_GENERIC_READ,
io::Write => libc::FILE_GENERIC_WRITE,
io::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
};
// libuv has a good comment about this, but the basic idea is what we try to
// emulate unix semantics by enabling all sharing by allowing things such as
// deleting a file while it's still open.
let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
libc::FILE_SHARE_DELETE;
let dwCreationDisposition = match (fm, fa) {
(io::Truncate, io::Read) => libc::TRUNCATE_EXISTING,
(io::Truncate, _) => libc::CREATE_ALWAYS,
(io::Open, io::Read) => libc::OPEN_EXISTING,
(io::Open, _) => libc::CREATE_NEW,
(io::Append, io::Read) => {
dwDesiredAccess |= libc::FILE_APPEND_DATA;
libc::OPEN_EXISTING
}
(io::Append, _) => {
dwDesiredAccess &= !libc::FILE_WRITE_DATA;
dwDesiredAccess |= libc::FILE_APPEND_DATA;
libc::OPEN_ALWAYS
}
};
let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
// Compat with unix, this allows opening directories (see libuv)
dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
let handle = as_utf16_p(path.as_str().unwrap(), |buf| unsafe {
libc::CreateFileW(buf,
dwDesiredAccess,
dwShareMode,
ptr::mut_null(),
dwCreationDisposition,
dwFlagsAndAttributes,
ptr::mut_null())
});
if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
Err(super::last_error())
} else {
let fd = unsafe {
libc::open_osfhandle(handle as libc::intptr_t, flags)
};
if fd < 0 {
let _ = unsafe { libc::CloseHandle(handle) };
Err(super::last_error())
} else {
Ok(FileDesc::new(fd, true))
}
}
}
pub fn mkdir(p: &CString, _mode: io::FilePermission) -> IoResult<()> {
super::mkerr_winbool(unsafe {
// FIXME: turn mode into something useful? #2623
as_utf16_p(p.as_str().unwrap(), |buf| {
libc::CreateDirectoryW(buf, ptr::mut_null())
})
})
}
pub fn readdir(p: &CString) -> IoResult<~[Path]> {
use rt::global_heap::malloc_raw;
fn prune(root: &CString, dirs: ~[Path]) -> ~[Path] {
let root = unsafe { CString::new(root.with_ref(|p| p), false) };
let root = Path::new(root);
dirs.move_iter().filter(|path| {
path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
}).map(|path| root.join(path)).collect()
}
#[nolink]
extern {
fn rust_list_dir_wfd_size() -> libc::size_t;
fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
}
let star = Path::new(unsafe {
CString::new(p.with_ref(|p| p), false)
}).join("*");
as_utf16_p(star.as_str().unwrap(), |path_ptr| unsafe {
let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
let find_handle = libc::FindFirstFileW(path_ptr, wfd_ptr as libc::HANDLE);
if find_handle as libc::c_int != libc::INVALID_HANDLE_VALUE {
let mut paths = ~[];
let mut more_files = 1 as libc::c_int;
while more_files != 0 {
let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr as *c_void);
if fp_buf as uint == 0 {
fail!("os::list_dir() failure: got null ptr from wfd");
} else {
let fp_vec = vec::from_buf(fp_buf,
libc::wcslen(fp_buf) as uint);
let fp_trimmed = str::truncate_utf16_at_nul(fp_vec);
let fp_str = str::from_utf16(fp_trimmed)
.expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16");
paths.push(Path::new(fp_str));
}
more_files = libc::FindNextFileW(find_handle,
wfd_ptr as libc::HANDLE);
}
assert!(libc::FindClose(find_handle) != 0);
libc::free(wfd_ptr as *mut c_void);
Ok(prune(p, paths))
} else {
Err(super::last_error())
}
})
}
pub fn unlink(p: &CString) -> IoResult<()> {
super::mkerr_winbool(unsafe {
as_utf16_p(p.as_str().unwrap(), |buf| {
libc::DeleteFileW(buf)
})
})
}
pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
super::mkerr_winbool(unsafe {
as_utf16_p(old.as_str().unwrap(), |old| {
as_utf16_p(new.as_str().unwrap(), |new| {
libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING)
})
})
})
}
pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> {
super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
libc::wchmod(p, mode as libc::c_int)
}))
}
pub fn rmdir(p: &CString) -> IoResult<()> {
super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
libc::wrmdir(p)
}))
}
pub fn chown(_p: &CString, _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<Path> {
// FIXME: I have a feeling that this reads intermediate symlinks as well.
let handle = unsafe {
as_utf16_p(p.as_str().unwrap(), |p| {
libc::CreateFileW(p,
libc::GENERIC_READ,
libc::FILE_SHARE_READ,
ptr::mut_null(),
libc::OPEN_EXISTING,
libc::FILE_ATTRIBUTE_NORMAL,
ptr::mut_null())
})
};
if handle as int == libc::INVALID_HANDLE_VALUE as int {
return Err(super::last_error())
}
// Specify (sz - 1) because the documentation states that it's the size
// without the null pointer
let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
libc::GetFinalPathNameByHandleW(handle,
buf as *u16,
sz - 1,
libc::VOLUME_NAME_DOS)
});
let ret = match ret {
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<()> {
super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
as_utf16_p(dst.as_str().unwrap(), |dst| {
unsafe { libc::CreateSymbolicLinkW(dst, src, 0) }
}) as libc::BOOL
}))
}
pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
as_utf16_p(dst.as_str().unwrap(), |dst| {
unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) }
})
}))
}
fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
let path = unsafe { CString::new(path.with_ref(|p| p), false) };
let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
libc::S_IFREG => io::TypeFile,
libc::S_IFDIR => io::TypeDirectory,
libc::S_IFIFO => io::TypeNamedPipe,
libc::S_IFBLK => io::TypeBlockSpecial,
libc::S_IFLNK => io::TypeSymlink,
_ => io::TypeUnknown,
};
io::FileStat {
path: Path::new(path),
size: stat.st_size as u64,
kind: kind,
perm: (stat.st_mode) as io::FilePermission & io::AllPermissions,
created: stat.st_ctime as u64,
modified: stat.st_mtime as u64,
accessed: stat.st_atime as u64,
unstable: io::UnstableFileStat {
device: stat.st_dev as u64,
inode: stat.st_ino as u64,
rdev: stat.st_rdev as u64,
nlink: stat.st_nlink as u64,
uid: stat.st_uid as u64,
gid: stat.st_gid as u64,
blksize: 0,
blocks: 0,
flags: 0,
gen: 0,
}
}
}
pub fn stat(p: &CString) -> IoResult<io::FileStat> {
let mut stat: libc::stat = unsafe { mem::uninit() };
as_utf16_p(p.as_str().unwrap(), |up| {
match unsafe { libc::wstat(up, &mut stat) } {
0 => Ok(mkstat(&stat, p)),
_ => Err(super::last_error()),
}
})
}
pub fn lstat(_p: &CString) -> IoResult<io::FileStat> {
// FIXME: implementation is missing
Err(super::unimpl())
}
pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
let buf = libc::utimbuf {
actime: (atime / 1000) as libc::time64_t,
modtime: (mtime / 1000) as libc::time64_t,
};
super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
libc::wutime(p, &buf)
}))
}

View file

@ -42,10 +42,16 @@ pub use self::process::Process;
// Native I/O implementations
pub mod addrinfo;
pub mod file;
pub mod net;
pub mod process;
#[cfg(unix)]
#[path = "file_unix.rs"]
pub mod file;
#[cfg(windows)]
#[path = "file_win32.rs"]
pub mod file;
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
#[cfg(target_os = "android")]
@ -97,7 +103,7 @@ fn translate_error(errno: i32, detail: bool) -> IoError {
libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"),
libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"),
libc::ERROR_BROKEN_PIPE => (io::BrokenPipe, "the pipe has ended"),
libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"),
x => {
debug!("ignoring {}: {}", x, os::last_os_error());
@ -185,6 +191,24 @@ fn retry(f: || -> libc::c_int) -> libc::c_int {
}
}
fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 {
let origamt = data.len();
let mut data = data.as_ptr();
let mut amt = origamt;
while amt > 0 {
let ret = retry(|| f(data, amt) as libc::c_int);
if ret == 0 {
break
} else if ret != -1 {
amt -= ret as uint;
data = unsafe { data.offset(ret as int) };
} else {
return ret as i64;
}
}
return (origamt - amt) as i64;
}
/// Implementation of rt::rtio's IoFactory trait to generate handles to the
/// native I/O functionality.
pub struct IoFactory {

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[allow(non_camel_case_types)];
use std::cast;
use std::io::net::ip;
use std::io;
@ -18,8 +16,7 @@ use std::mem;
use std::rt::rtio;
use std::sync::arc::UnsafeArc;
use super::{IoResult, retry};
use super::file::keep_going;
use super::{IoResult, retry, keep_going};
////////////////////////////////////////////////////////////////////////////////
// sockaddr and misc bindings
@ -323,16 +320,14 @@ impl rtio::RtioTcpStream for TcpStream {
}
}
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let ret = keep_going(buf, |buf, len| {
unsafe {
libc::send(self.fd(),
buf as *mut libc::c_void,
len as wrlen,
0) as i64
}
let ret = keep_going(buf, |buf, len| unsafe {
libc::send(self.fd(),
buf as *mut libc::c_void,
len as wrlen,
0) as i64
});
if ret < 0 {
Err(last_error())
Err(super::last_error())
} else {
Ok(())
}

View file

@ -17,8 +17,8 @@ use std::rt::rtio;
use std::sync::arc::UnsafeArc;
use std::intrinsics;
use super::{IoResult, retry};
use super::file::{keep_going, fd_t};
use super::{IoResult, retry, keep_going};
use super::file::fd_t;
fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> {
match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } {

View file

@ -20,8 +20,6 @@
//! can be created in the future and there must be no active timers at that
//! time.
#[allow(non_camel_case_types)];
use std::cast;
use std::rt;
use std::unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
@ -100,7 +98,6 @@ mod imp {
use io::file::FileDesc;
#[allow(non_camel_case_types)]
pub type signal = libc::c_int;
pub fn new() -> (signal, signal) {

View file

@ -46,8 +46,6 @@
//!
//! Note that all time units in this file are in *milliseconds*.
#[allow(non_camel_case_types)];
use std::comm::Data;
use std::libc;
use std::mem;

View file

@ -28,8 +28,6 @@
//!
//! As with timer_other, all units in this file are in units of millseconds.
#[allow(non_camel_case_types)];
use std::comm::Data;
use std::libc;
use std::ptr;

View file

@ -49,6 +49,7 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://static.rust-lang.org/doc/master")];
#[deny(unused_result, unused_must_use)];
#[allow(non_camel_case_types)];
// NB this crate explicitly does *not* allow glob imports, please seriously
// consider whether they're needed before adding that feature here (the

View file

@ -1745,6 +1745,10 @@ pub mod consts {
pub static OPEN_EXISTING: DWORD = 3;
pub static TRUNCATE_EXISTING: DWORD = 5;
pub static FILE_APPEND_DATA: DWORD = 0x00000004;
pub static FILE_READ_DATA: DWORD = 0x00000001;
pub static FILE_WRITE_DATA: DWORD = 0x00000002;
pub static FILE_ATTRIBUTE_ARCHIVE: DWORD = 0x20;
pub static FILE_ATTRIBUTE_COMPRESSED: DWORD = 0x800;
pub static FILE_ATTRIBUTE_DEVICE: DWORD = 0x40;
@ -1791,6 +1795,18 @@ pub mod consts {
pub static FILE_WRITE_ATTRIBUTES: DWORD = 0x00000100;
pub static FILE_READ_ATTRIBUTES: DWORD = 0x00000080;
pub static STANDARD_RIGHTS_READ: DWORD = 0x20000;
pub static STANDARD_RIGHTS_WRITE: DWORD = 0x20000;
pub static FILE_WRITE_EA: DWORD = 0x00000010;
pub static FILE_READ_EA: DWORD = 0x00000008;
pub static FILE_GENERIC_READ: DWORD =
STANDARD_RIGHTS_READ | FILE_READ_DATA |
FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE;
pub static FILE_GENERIC_WRITE: DWORD =
STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA |
SYNCHRONIZE;
pub static FILE_BEGIN: DWORD = 0;
pub static FILE_CURRENT: DWORD = 1;
pub static FILE_END: DWORD = 2;
@ -4231,6 +4247,7 @@ pub mod funcs {
pub mod msvcrt {
use libc::types::os::arch::c95::{c_int, c_long};
use libc::types::os::arch::c99::intptr_t;
#[nolink]
extern {
@ -4239,6 +4256,10 @@ pub mod funcs {
#[link_name = "_get_osfhandle"]
pub fn get_osfhandle(fd: c_int) -> c_long;
#[link_name = "_open_osfhandle"]
pub fn open_osfhandle(osfhandle: intptr_t,
flags: c_int) -> c_int;
}
}
}