diff --git a/mk/crates.mk b/mk/crates.mk index 09b1dd699105b..45b6ed1a058d0 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -81,8 +81,8 @@ DEPS_test := std extra collections getopts serialize term DEPS_time := std serialize TOOL_DEPS_compiletest := test green rustuv getopts -TOOL_DEPS_rustdoc := rustdoc green rustuv -TOOL_DEPS_rustc := rustc green rustuv +TOOL_DEPS_rustdoc := rustdoc native +TOOL_DEPS_rustc := rustc native TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs TOOL_SOURCE_rustdoc := $(S)src/driver/driver.rs TOOL_SOURCE_rustc := $(S)src/driver/driver.rs diff --git a/src/driver/driver.rs b/src/driver/driver.rs index ca462fc7a3922..0ceb12064b0eb 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -8,10 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[no_uv]; + #[cfg(rustdoc)] extern crate this = "rustdoc"; #[cfg(rustc)] extern crate this = "rustc"; -fn main() { this::main() } +extern crate native; + +#[start] +fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, this::main) } diff --git a/src/libnative/io/file.rs b/src/libnative/io/file.rs deleted file mode 100644 index 27430ddee97eb..0000000000000 --- a/src/libnative/io/file.rs +++ /dev/null @@ -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 or the MIT license -// , 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 -} - -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 { - #[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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - return os_readlink(p); - - // FIXME: I have a feeling that this reads intermediate symlinks as well. - #[cfg(windows)] - fn os_readlink(p: &CString) -> IoResult { - 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 { - 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::(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 { - return os_stat(p); - - #[cfg(windows)] - fn os_stat(p: &CString) -> IoResult { - 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 { - 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 { - return os_lstat(p); - - // FIXME: windows implementation is missing - #[cfg(windows)] - fn os_lstat(_p: &CString) -> IoResult { - Err(super::unimpl()) - } - - #[cfg(unix)] - fn os_lstat(p: &CString) -> IoResult { - 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) - } - } - } -} diff --git a/src/libnative/io/file_unix.rs b/src/libnative/io/file_unix.rs new file mode 100644 index 0000000000000..4b6d1813ffa4a --- /dev/null +++ b/src/libnative/io/file_unix.rs @@ -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 or the MIT license +// , 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 +} + +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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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::(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 { + 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 { + 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) + } + } + } +} + diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_win32.rs new file mode 100644 index 0000000000000..e880bd05cf7fc --- /dev/null +++ b/src/libnative/io/file_win32.rs @@ -0,0 +1,517 @@ +// 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 or the MIT license +// , 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 +} + +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 { + 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 { + 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 { + 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 { + 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 { + 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 { + // 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 { + 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 { + 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 { + // 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 { + // 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(ref s) if s.starts_with(r"\\?\") => Ok(Path::new(s.slice_from(4))), + Some(s) => Ok(Path::new(s)), + None => Err(super::last_error()), + }; + assert!(unsafe { libc::CloseHandle(handle) } != 0); + return ret; +} + +pub fn symlink(src: &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 { + 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 { + // 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) + })) +} diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 2f4dc7817d353..2e3e9b3b50637 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -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,14 @@ 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"), + + // libuv maps this error code to EISDIR. we do too. if it is found + // to be incorrect, we can add in some more machinery to only + // return this message when ERROR_INVALID_FUNCTION after certain + // win32 calls. + libc::ERROR_INVALID_FUNCTION => (io::InvalidInput, + "illegal operation on a directory"), x => { debug!("ignoring {}: {}", x, os::last_os_error()); @@ -121,6 +134,7 @@ fn translate_error(errno: i32, detail: bool) -> IoError { libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"), libc::EADDRINUSE => (io::ConnectionRefused, "address in use"), libc::ENOENT => (io::FileNotFound, "no such file or directory"), + libc::EISDIR => (io::InvalidInput, "illegal operation on a directory"), // These two constants can have the same value on some systems, but // different values on others, so we can't use a match clause @@ -185,6 +199,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 { diff --git a/src/libnative/io/net.rs b/src/libnative/io/net.rs index d71f7544225c6..7445e4c099261 100644 --- a/src/libnative/io/net.rs +++ b/src/libnative/io/net.rs @@ -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(()) } diff --git a/src/libnative/io/pipe_unix.rs b/src/libnative/io/pipe_unix.rs index 784d8650689a4..9e81dc02cc596 100644 --- a/src/libnative/io/pipe_unix.rs +++ b/src/libnative/io/pipe_unix.rs @@ -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 { match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } { diff --git a/src/libnative/io/timer_helper.rs b/src/libnative/io/timer_helper.rs index 0f3ed1482294b..7669d4a658fad 100644 --- a/src/libnative/io/timer_helper.rs +++ b/src/libnative/io/timer_helper.rs @@ -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) { diff --git a/src/libnative/io/timer_other.rs b/src/libnative/io/timer_other.rs index 9f332adb27b05..0784b5ee04845 100644 --- a/src/libnative/io/timer_other.rs +++ b/src/libnative/io/timer_other.rs @@ -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; diff --git a/src/libnative/io/timer_timerfd.rs b/src/libnative/io/timer_timerfd.rs index baafe3f4850c1..7feeaa4768c9a 100644 --- a/src/libnative/io/timer_timerfd.rs +++ b/src/libnative/io/timer_timerfd.rs @@ -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; diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index c28a1175494f9..238e4c23b452e 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -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 diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index ffccb0e8cb152..5f070ef782b4e 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -663,6 +663,13 @@ mod test { use io; use ops::Drop; + macro_rules! check( ($e:expr) => ( + match $e { + Ok(t) => t, + Err(e) => fail!("{} failed with: {}", stringify!($e), e), + } + ) ) + struct TempDir(Path); impl TempDir { @@ -682,7 +689,7 @@ mod test { // Gee, seeing how we're testing the fs module I sure hope that we // at least implement this correctly! let TempDir(ref p) = *self; - io::fs::rmdir_recursive(p).unwrap(); + check!(io::fs::rmdir_recursive(p)); } } @@ -690,7 +697,7 @@ mod test { use os; use rand; let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); - io::fs::mkdir(&ret, io::UserRWX).unwrap(); + check!(io::fs::mkdir(&ret, io::UserRWX)); TempDir(ret) } @@ -700,18 +707,18 @@ mod test { let filename = &tmpdir.join("file_rt_io_file_test.txt"); { let mut write_stream = File::open_mode(filename, Open, ReadWrite); - write_stream.write(message.as_bytes()).unwrap(); + check!(write_stream.write(message.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); let mut read_buf = [0, .. 1028]; - let read_str = match read_stream.read(read_buf).unwrap() { + let read_str = match check!(read_stream.read(read_buf)) { -1|0 => fail!("shouldn't happen"), - n => str::from_utf8_owned(read_buf.slice_to(n).to_owned()).unwrap() + n => str::from_utf8(read_buf.slice_to(n).to_owned()).unwrap().to_owned() }; assert_eq!(read_str, message.to_owned()); } - unlink(filename).unwrap(); + check!(unlink(filename)); }) iotest!(fn invalid_path_raises() { @@ -734,20 +741,20 @@ mod test { let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(message.as_bytes()).unwrap(); + check!(rw_stream.write(message.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); { let read_buf = read_mem.mut_slice(0, 4); - read_stream.read(read_buf).unwrap(); + check!(read_stream.read(read_buf)); } { let read_buf = read_mem.mut_slice(4, 8); - read_stream.read(read_buf).unwrap(); + check!(read_stream.read(read_buf)); } } - unlink(filename).unwrap(); + check!(unlink(filename)); let read_str = str::from_utf8(read_mem).unwrap(); assert_eq!(read_str, message); }) @@ -762,16 +769,16 @@ mod test { let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(message.as_bytes()).unwrap(); + check!(rw_stream.write(message.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.seek(set_cursor as i64, SeekSet).unwrap(); - tell_pos_pre_read = read_stream.tell().unwrap(); - read_stream.read(read_mem).unwrap(); - tell_pos_post_read = read_stream.tell().unwrap(); + check!(read_stream.seek(set_cursor as i64, SeekSet)); + tell_pos_pre_read = check!(read_stream.tell()); + check!(read_stream.read(read_mem)); + tell_pos_post_read = check!(read_stream.tell()); } - unlink(filename).unwrap(); + check!(unlink(filename)); let read_str = str::from_utf8(read_mem).unwrap(); assert_eq!(read_str, message.slice(4, 8)); assert_eq!(tell_pos_pre_read, set_cursor); @@ -788,15 +795,15 @@ mod test { let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(initial_msg.as_bytes()).unwrap(); - rw_stream.seek(seek_idx as i64, SeekSet).unwrap(); - rw_stream.write(overwrite_msg.as_bytes()).unwrap(); + check!(rw_stream.write(initial_msg.as_bytes())); + check!(rw_stream.seek(seek_idx as i64, SeekSet)); + check!(rw_stream.write(overwrite_msg.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.read(read_mem).unwrap(); + check!(read_stream.read(read_mem)); } - unlink(filename).unwrap(); + check!(unlink(filename)); let read_str = str::from_utf8(read_mem).unwrap(); assert!(read_str == final_msg.to_owned()); }) @@ -812,24 +819,24 @@ mod test { let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(initial_msg.as_bytes()).unwrap(); + check!(rw_stream.write(initial_msg.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.seek(-4, SeekEnd).unwrap(); - read_stream.read(read_mem).unwrap(); + check!(read_stream.seek(-4, SeekEnd)); + check!(read_stream.read(read_mem)); assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_three); - read_stream.seek(-9, SeekCur).unwrap(); - read_stream.read(read_mem).unwrap(); + check!(read_stream.seek(-9, SeekCur)); + check!(read_stream.read(read_mem)); assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_two); - read_stream.seek(0, SeekSet).unwrap(); - read_stream.read(read_mem).unwrap(); + check!(read_stream.seek(0, SeekSet)); + check!(read_stream.read(read_mem)); assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_one); } - unlink(filename).unwrap(); + check!(unlink(filename)); }) iotest!(fn file_test_stat_is_correct_on_is_file() { @@ -840,34 +847,34 @@ mod test { let msg = "hw"; fs.write(msg.as_bytes()).unwrap(); } - let stat_res = stat(filename).unwrap(); + let stat_res = check!(stat(filename)); assert_eq!(stat_res.kind, io::TypeFile); - unlink(filename).unwrap(); + check!(unlink(filename)); }) iotest!(fn file_test_stat_is_correct_on_is_dir() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_stat_correct_on_is_dir"); - mkdir(filename, io::UserRWX).unwrap(); - let stat_res = filename.stat().unwrap(); + check!(mkdir(filename, io::UserRWX)); + let stat_res = check!(filename.stat()); assert!(stat_res.kind == io::TypeDirectory); - rmdir(filename).unwrap(); + check!(rmdir(filename)); }) iotest!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { let tmpdir = tmpdir(); let dir = &tmpdir.join("fileinfo_false_on_dir"); - mkdir(dir, io::UserRWX).unwrap(); + check!(mkdir(dir, io::UserRWX)); assert!(dir.is_file() == false); - rmdir(dir).unwrap(); + check!(rmdir(dir)); }) iotest!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() { let tmpdir = tmpdir(); let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); - File::create(file).write(bytes!("foo")).unwrap(); + check!(File::create(file).write(bytes!("foo"))); assert!(file.exists()); - unlink(file).unwrap(); + check!(unlink(file)); assert!(!file.exists()); }) @@ -875,10 +882,10 @@ mod test { let tmpdir = tmpdir(); let dir = &tmpdir.join("before_and_after_dir"); assert!(!dir.exists()); - mkdir(dir, io::UserRWX).unwrap(); + check!(mkdir(dir, io::UserRWX)); assert!(dir.exists()); assert!(dir.is_dir()); - rmdir(dir).unwrap(); + check!(rmdir(dir)); assert!(!dir.exists()); }) @@ -886,21 +893,21 @@ mod test { use std::str; let tmpdir = tmpdir(); let dir = &tmpdir.join("di_readdir"); - mkdir(dir, io::UserRWX).unwrap(); + check!(mkdir(dir, io::UserRWX)); let prefix = "foo"; for n in range(0,3) { let f = dir.join(format!("{}.txt", n)); - let mut w = File::create(&f).unwrap(); + let mut w = check!(File::create(&f)); let msg_str = (prefix + n.to_str().to_owned()).to_owned(); let msg = msg_str.as_bytes(); - w.write(msg).unwrap(); + check!(w.write(msg)); } - let files = readdir(dir).unwrap(); + let files = check!(readdir(dir)); let mut mem = [0u8, .. 4]; for f in files.iter() { { let n = f.filestem_str(); - File::open(f).read(mem).unwrap(); + check!(File::open(f).read(mem)); let read_str = str::from_utf8(mem).unwrap(); let expected = match n { None|Some("") => fail!("really shouldn't happen.."), @@ -908,13 +915,13 @@ mod test { }; assert_eq!(expected.as_slice(), read_str); } - unlink(f).unwrap(); + check!(unlink(f)); } - rmdir(dir).unwrap(); + check!(rmdir(dir)); }) iotest!(fn recursive_mkdir_slash() { - mkdir_recursive(&Path::new("/"), io::UserRWX).unwrap(); + check!(mkdir_recursive(&Path::new("/"), io::UserRWX)); }) iotest!(fn unicode_path_is_dir() { @@ -925,12 +932,12 @@ mod test { let mut dirpath = tmpdir.path().clone(); dirpath.push(format!("test-가一ー你好")); - mkdir(&dirpath, io::UserRWX).unwrap(); + check!(mkdir(&dirpath, io::UserRWX)); assert!(dirpath.is_dir()); let mut filepath = dirpath; filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); - File::create(&filepath).unwrap(); // ignore return; touch only + check!(File::create(&filepath)); // ignore return; touch only assert!(!filepath.is_dir()); assert!(filepath.exists()); }) @@ -942,7 +949,7 @@ mod test { let tmpdir = tmpdir(); let unicode = tmpdir.path(); let unicode = unicode.join(format!("test-각丁ー再见")); - mkdir(&unicode, io::UserRWX).unwrap(); + check!(mkdir(&unicode, io::UserRWX)); assert!(unicode.exists()); assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); }) @@ -964,19 +971,19 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).write(bytes!("hello")).unwrap(); - copy(&input, &out).unwrap(); - let contents = File::open(&out).read_to_end().unwrap(); + check!(File::create(&input).write(bytes!("hello"))); + check!(copy(&input, &out)); + let contents = check!(File::open(&out).read_to_end()); assert_eq!(contents.as_slice(), bytes!("hello")); - assert_eq!(input.stat().unwrap().perm, out.stat().unwrap().perm); + assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm); }) iotest!(fn copy_file_dst_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); - File::create(&out).unwrap(); + check!(File::create(&out)); match copy(&out, tmpdir.path()) { Ok(..) => fail!(), Err(..) => {} } @@ -987,11 +994,11 @@ mod test { let input = tmpdir.join("in"); let output = tmpdir.join("out"); - File::create(&input).write("foo".as_bytes()).unwrap(); - File::create(&output).write("bar".as_bytes()).unwrap(); - copy(&input, &output).unwrap(); + check!(File::create(&input).write("foo".as_bytes())); + check!(File::create(&output).write("bar".as_bytes())); + check!(copy(&input, &output)); - assert_eq!(File::open(&output).read_to_end().unwrap(), + assert_eq!(check!(File::open(&output).read_to_end()), (bytes!("foo")).to_owned()); }) @@ -1010,13 +1017,13 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).unwrap(); - chmod(&input, io::UserRead).unwrap(); - copy(&input, &out).unwrap(); - assert!(out.stat().unwrap().perm & io::UserWrite == 0); + check!(File::create(&input)); + check!(chmod(&input, io::UserRead)); + check!(copy(&input, &out)); + assert!(check!(out.stat()).perm & io::UserWrite == 0); - chmod(&input, io::UserFile).unwrap(); - chmod(&out, io::UserFile).unwrap(); + check!(chmod(&input, io::UserFile)); + check!(chmod(&out, io::UserFile)); }) #[cfg(not(windows))] // FIXME(#10264) operation not permitted? @@ -1025,13 +1032,13 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).write("foobar".as_bytes()).unwrap(); - symlink(&input, &out).unwrap(); + check!(File::create(&input).write("foobar".as_bytes())); + check!(symlink(&input, &out)); if cfg!(not(windows)) { - assert_eq!(lstat(&out).unwrap().kind, io::TypeSymlink); + assert_eq!(check!(lstat(&out)).kind, io::TypeSymlink); } - assert_eq!(stat(&out).unwrap().size, stat(&input).unwrap().size); - assert_eq!(File::open(&out).read_to_end().unwrap(), + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(File::open(&out).read_to_end()), (bytes!("foobar")).to_owned()); }) @@ -1039,8 +1046,8 @@ mod test { iotest!(fn symlink_noexist() { let tmpdir = tmpdir(); // symlinks can point to things that don't exist - symlink(&tmpdir.join("foo"), &tmpdir.join("bar")).unwrap(); - assert!(readlink(&tmpdir.join("bar")).unwrap() == tmpdir.join("foo")); + check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar"))); + assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo")); }) iotest!(fn readlink_not_symlink() { @@ -1056,14 +1063,14 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).write("foobar".as_bytes()).unwrap(); - link(&input, &out).unwrap(); + check!(File::create(&input).write("foobar".as_bytes())); + check!(link(&input, &out)); if cfg!(not(windows)) { - assert_eq!(lstat(&out).unwrap().kind, io::TypeFile); - assert_eq!(stat(&out).unwrap().unstable.nlink, 2); + assert_eq!(check!(lstat(&out)).kind, io::TypeFile); + assert_eq!(check!(stat(&out)).unstable.nlink, 2); } - assert_eq!(stat(&out).unwrap().size, stat(&input).unwrap().size); - assert_eq!(File::open(&out).read_to_end().unwrap(), + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(File::open(&out).read_to_end()), (bytes!("foobar")).to_owned()); // can't link to yourself @@ -1082,62 +1089,62 @@ mod test { let tmpdir = tmpdir(); let file = tmpdir.join("in.txt"); - File::create(&file).unwrap(); - assert!(stat(&file).unwrap().perm & io::UserWrite == io::UserWrite); - chmod(&file, io::UserRead).unwrap(); - assert!(stat(&file).unwrap().perm & io::UserWrite == 0); + check!(File::create(&file)); + assert!(check!(stat(&file)).perm & io::UserWrite == io::UserWrite); + check!(chmod(&file, io::UserRead)); + assert!(check!(stat(&file)).perm & io::UserWrite == 0); match chmod(&tmpdir.join("foo"), io::UserRWX) { Ok(..) => fail!("wanted a failure"), Err(..) => {} } - chmod(&file, io::UserFile).unwrap(); + check!(chmod(&file, io::UserFile)); }) iotest!(fn sync_doesnt_kill_anything() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); - let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap(); - file.fsync().unwrap(); - file.datasync().unwrap(); - file.write(bytes!("foo")).unwrap(); - file.fsync().unwrap(); - file.datasync().unwrap(); + let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite)); + check!(file.fsync()); + check!(file.datasync()); + check!(file.write(bytes!("foo"))); + check!(file.fsync()); + check!(file.datasync()); drop(file); - } #[ignore(cfg(windows))]) + }) iotest!(fn truncate_works() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); - let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap(); - file.write(bytes!("foo")).unwrap(); - file.fsync().unwrap(); + let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite)); + check!(file.write(bytes!("foo"))); + check!(file.fsync()); // Do some simple things with truncation - assert_eq!(stat(&path).unwrap().size, 3); - file.truncate(10).unwrap(); - assert_eq!(stat(&path).unwrap().size, 10); - file.write(bytes!("bar")).unwrap(); - file.fsync().unwrap(); - assert_eq!(stat(&path).unwrap().size, 10); - assert_eq!(File::open(&path).read_to_end().unwrap(), + assert_eq!(check!(stat(&path)).size, 3); + check!(file.truncate(10)); + assert_eq!(check!(stat(&path)).size, 10); + check!(file.write(bytes!("bar"))); + check!(file.fsync()); + assert_eq!(check!(stat(&path)).size, 10); + assert_eq!(check!(File::open(&path).read_to_end()), (bytes!("foobar", 0, 0, 0, 0)).to_owned()); // Truncate to a smaller length, don't seek, and then write something. // Ensure that the intermediate zeroes are all filled in (we're seeked // past the end of the file). - file.truncate(2).unwrap(); - assert_eq!(stat(&path).unwrap().size, 2); - file.write(bytes!("wut")).unwrap(); - file.fsync().unwrap(); - assert_eq!(stat(&path).unwrap().size, 9); - assert_eq!(File::open(&path).read_to_end().unwrap(), + check!(file.truncate(2)); + assert_eq!(check!(stat(&path)).size, 2); + check!(file.write(bytes!("wut"))); + check!(file.fsync()); + assert_eq!(check!(stat(&path)).size, 9); + assert_eq!(check!(File::open(&path).read_to_end()), (bytes!("fo", 0, 0, 0, 0, "wut")).to_owned()); drop(file); - } #[ignore(cfg(windows))]) // FIXME(#11638) + }) iotest!(fn open_flavors() { let tmpdir = tmpdir(); @@ -1145,46 +1152,49 @@ mod test { match File::open_mode(&tmpdir.join("a"), io::Open, io::Read) { Ok(..) => fail!(), Err(..) => {} } - File::open_mode(&tmpdir.join("b"), io::Open, io::Write).unwrap(); - File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite).unwrap(); - File::open_mode(&tmpdir.join("d"), io::Append, io::Write).unwrap(); - File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite).unwrap(); - File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write).unwrap(); - File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite).unwrap(); - - File::create(&tmpdir.join("h")).write("foo".as_bytes()).unwrap(); - File::open_mode(&tmpdir.join("h"), io::Open, io::Read).unwrap(); + check!(File::open_mode(&tmpdir.join("b"), io::Open, io::Write)); + check!(File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite)); + check!(File::open_mode(&tmpdir.join("d"), io::Append, io::Write)); + check!(File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite)); + check!(File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write)); + check!(File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite)); + + check!(File::create(&tmpdir.join("h")).write("foo".as_bytes())); + check!(File::open_mode(&tmpdir.join("h"), io::Open, io::Read)); { - let mut f = File::open_mode(&tmpdir.join("h"), io::Open, - io::Read).unwrap(); + let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Open, + io::Read)); match f.write("wut".as_bytes()) { Ok(..) => fail!(), Err(..) => {} } } - assert_eq!(stat(&tmpdir.join("h")).unwrap().size, 3); + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "write/stat failed"); { - let mut f = File::open_mode(&tmpdir.join("h"), io::Append, - io::Write).unwrap(); - f.write("bar".as_bytes()).unwrap(); + let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Append, + io::Write)); + check!(f.write("bar".as_bytes())); } - assert_eq!(stat(&tmpdir.join("h")).unwrap().size, 6); + assert!(check!(stat(&tmpdir.join("h"))).size == 6, + "append didn't append"); { - let mut f = File::open_mode(&tmpdir.join("h"), io::Truncate, - io::Write).unwrap(); - f.write("bar".as_bytes()).unwrap(); + let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Truncate, + io::Write)); + check!(f.write("bar".as_bytes())); } - assert_eq!(stat(&tmpdir.join("h")).unwrap().size, 3); + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "truncate didn't truncate"); }) #[test] fn utime() { let tmpdir = tmpdir(); let path = tmpdir.join("a"); - File::create(&path).unwrap(); + check!(File::create(&path)); - change_file_times(&path, 1000, 2000).unwrap(); - assert_eq!(path.stat().unwrap().accessed, 1000); - assert_eq!(path.stat().unwrap().modified, 2000); + check!(change_file_times(&path, 1000, 2000)); + assert_eq!(check!(path.stat()).accessed, 1000); + assert_eq!(check!(path.stat()).modified, 2000); } #[test] @@ -1196,4 +1206,17 @@ mod test { Err(..) => {} } } + + iotest!(fn binary_file() { + use rand::{rng, Rng}; + + let mut bytes = [0, ..1024]; + rng().fill_bytes(bytes); + + let tmpdir = tmpdir(); + + check!(File::create(&tmpdir.join("test")).write(bytes)); + let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); + assert!(actual.as_slice() == bytes); + }) } diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index c3e3d72c6721e..4545fea061b8f 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -137,16 +137,20 @@ mod tests { pub fn smalltest(server: proc(UnixStream), client: proc(UnixStream)) { let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = Chan::new(); + + let mut acceptor = UnixListener::bind(&path1).listen(); spawn(proc() { - port.recv(); - client(UnixStream::connect(&path2).unwrap()); + match UnixStream::connect(&path2) { + Ok(c) => client(c), + Err(e) => fail!("failed connect: {}", e), + } }); - let mut acceptor = UnixListener::bind(&path1).listen(); - chan.send(()); - server(acceptor.accept().unwrap()); + match acceptor.accept() { + Ok(c) => server(c), + Err(e) => fail!("failed accept: {}", e), + } } iotest!(fn bind_error() { diff --git a/src/libstd/io/signal.rs b/src/libstd/io/signal.rs index 32670fa7c2c24..43419c751fcce 100644 --- a/src/libstd/io/signal.rs +++ b/src/libstd/io/signal.rs @@ -146,22 +146,20 @@ impl Listener { } } -#[cfg(test)] -mod test { +#[cfg(test, unix)] +mod test_unix { use libc; use comm::Empty; use io::timer; use super::{Listener, Interrupt}; - // kill is only available on Unixes - #[cfg(unix)] fn sigint() { unsafe { libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT); } } - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) + #[test] #[cfg(not(target_os="android"))] // FIXME(#10378) fn test_io_signal_smoketest() { let mut signal = Listener::new(); signal.register(Interrupt).unwrap(); @@ -173,7 +171,7 @@ mod test { } } - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) + #[test] #[cfg(not(target_os="android"))] // FIXME(#10378) fn test_io_signal_two_signal_one_signum() { let mut s1 = Listener::new(); let mut s2 = Listener::new(); @@ -191,7 +189,7 @@ mod test { } } - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) + #[test] #[cfg(not(target_os="android"))] // FIXME(#10378) fn test_io_signal_unregister() { let mut s1 = Listener::new(); let mut s2 = Listener::new(); @@ -202,15 +200,16 @@ mod test { timer::sleep(10); assert_eq!(s2.port.try_recv(), Empty); } +} + +#[cfg(test, windows)] +mod test_windows { + use super::{User1, Listener}; + use result::{Ok, Err}; - #[cfg(windows)] #[test] fn test_io_signal_invalid_signum() { - use io; - use super::User1; - use result::{Ok, Err}; let mut s = Listener::new(); - let mut called = false; match s.register(User1) { Ok(..) => { fail!("Unexpected successful registry of signum {:?}", User1); diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index 73bf4a1e69a88..07be753925f20 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -1623,6 +1623,7 @@ pub mod consts { pub static O_NOINHERIT: c_int = 128; pub static ERROR_SUCCESS : c_int = 0; + pub static ERROR_INVALID_FUNCTION: c_int = 1; pub static ERROR_FILE_NOT_FOUND: c_int = 2; pub static ERROR_ACCESS_DENIED: c_int = 5; pub static ERROR_INVALID_HANDLE : c_int = 6; @@ -1745,6 +1746,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 +1796,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 +4248,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 +4257,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; } } } diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index d1af612e0ec5e..a8f7782fa4623 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -698,8 +698,8 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr) => ( { - let path = ($path); - let join = ($join); + let path = $path; + let join = $join; let mut p1 = Path::new(path); let p2 = p1.clone(); p1.push(join); diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 8902ab2edd744..10834aec64c94 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -1433,8 +1433,8 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr) => ( { - let path = ($path); - let join = ($join); + let path = $path; + let join = $join; let mut p1 = Path::new(path); let p2 = p1.clone(); p1.push(join); diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs index 84fa528ebf130..57dbc045a65fe 100644 --- a/src/libstd/unstable/dynamic_lib.rs +++ b/src/libstd/unstable/dynamic_lib.rs @@ -82,9 +82,7 @@ impl DynamicLibrary { #[cfg(test)] mod test { use super::*; - use option::*; - use result::*; - use path::*; + use prelude::*; use libc; #[test] diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index cf49ea535623d..ce719c6d0b8db 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -3529,7 +3529,7 @@ mod tests { let mut v: [uint, .. 0] = []; v.sort(); - let mut v = [0xDEADBEEF]; + let mut v = [0xDEADBEEFu]; v.sort(); assert_eq!(v, [0xDEADBEEF]); }